From 194fd755733b40440a355cec3b6a61253df25c9b Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Tue, 25 Aug 2026 11:26:09 +0100 Subject: [PATCH 1/3] fix(instance-checkpoints): Show delete-lock at long verbosity It was hidden here but long on instance templates, for no reason other than oversight - the platform populates it identically for both. Signed-off-by: Justin Chadwell --- internal/cmd/instance_checkpoints.go | 2 +- .../testdata/TestOutput/instance-checkpoints | 31 ++++++++++--------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/internal/cmd/instance_checkpoints.go b/internal/cmd/instance_checkpoints.go index f4584100..188e3eeb 100644 --- a/internal/cmd/instance_checkpoints.go +++ b/internal/cmd/instance_checkpoints.go @@ -81,7 +81,7 @@ type InstanceCheckpoint struct { UUID string `mirror:"instance.uuid" field:",long"` Tags []string `mirror:"instance.tags" field:",long" edit:"set,add,del"` - DeleteLock bool `mirror:"instance.delete_lock" field:"delete-lock,hidden" edit:"set"` + DeleteLock bool `mirror:"instance.delete_lock" field:"delete-lock,long" edit:"set"` State types.InstanceState `mirror:"instance.state" field:",short"` Image types.ImageRef[reference.Named] `mirror:"instance.image" field:",short"` diff --git a/internal/cmd/testdata/TestOutput/instance-checkpoints b/internal/cmd/testdata/TestOutput/instance-checkpoints index 39d448ad..10fdeae9 100644 --- a/internal/cmd/testdata/TestOutput/instance-checkpoints +++ b/internal/cmd/testdata/TestOutput/instance-checkpoints @@ -1,22 +1,23 @@ ====================================== kv ====================================== -metro: fra -name: my-checkpoint -uuid: f6a7b8c9-d0e1-2345-f012-3456789abcde -tags: ["env-prod", "team-core"] -state: checkpoint -image: nginx +metro: fra +name: my-checkpoint +uuid: f6a7b8c9-d0e1-2345-f012-3456789abcde +tags: ["env-prod", "team-core"] +delete-lock: true +state: checkpoint +image: nginx runtime: - args: ["arg1", "arg2"] + args: ["arg1", "arg2"] env: - KEY1: val1 - KEY2: val2 + KEY1: val1 + KEY2: val2 resources: - memory: 256MiB - vcpus: 2 + memory: 256MiB + vcpus: 2 volumes: -- name: my-volume - at: /data - readonly: true +- name: my-volume + at: /data + readonly: true ==================================== kv-all ==================================== metro: fra @@ -96,7 +97,7 @@ fra my-checkpoint checkpoint nginx ["arg1", "arg2"] 256MiB 2 never { "name": "delete-lock", "value": true, - "verbosity": "hidden", + "verbosity": "long", "edit": { "set": true } From f3a290fcb18973a85201b20aa113e766c1b5db5a Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Tue, 25 Aug 2026 11:27:05 +0100 Subject: [PATCH 2/3] fix(instance-templates): Link volumes to volume templates Converting an instance to a template - or checkpointing one - turns its attached volumes into volume templates, and the two lookups are mutually exclusive: /v1/volumes/status excludes template-state volumes and /v1/volumes/templates/status requires them. So a Link[Volume] on either resource could only ever miss, breaking TUI drill-down and leaving the volumes untracked by the sandbox at teardown. Adds a read-only InstanceTemplateVolume carrying Link[VolumeTemplate] rather than reusing InstanceVolume, which also drops the create-only size subfield that has no meaning without a volume-create path. Integration tests cover both directions, including that a checkpoint gets its own cloned volume under a fresh UUID while the source volume is left alone. Signed-off-by: Justin Chadwell --- .../integration/instance_checkpoint_test.go | 70 +++++++++++++++++++ .../integration/instance_template_test.go | 56 +++++++++++++++ cmd/unikraft/testdata/TestHelp/instances | 26 +++---- internal/cmd/instance_checkpoints.go | 2 +- internal/cmd/instance_templates.go | 2 +- internal/cmd/instances.go | 9 +++ internal/cmd/output_test.go | 4 +- .../testdata/TestOutput/instance-checkpoints | 16 +---- .../testdata/TestOutput/instance-templates | 7 -- 9 files changed, 153 insertions(+), 39 deletions(-) diff --git a/cmd/unikraft/integration/instance_checkpoint_test.go b/cmd/unikraft/integration/instance_checkpoint_test.go index de8796a8..1c66fec7 100644 --- a/cmd/unikraft/integration/instance_checkpoint_test.go +++ b/cmd/unikraft/integration/instance_checkpoint_test.go @@ -169,6 +169,76 @@ func TestInstanceCheckpoints(t *testing.T) { r.Run(t, []string{"unikraft", "instance", "delete", "test-" + instName}) }) + // volumes proves a checkpoint gets its own cloned copy of the source + // instance's volumes, that the clone is a volume template rather than a + // plain volume, and that the source volume is left untouched. + t.Run("volumes", func(t *testing.T) { + r := runner(t, true, []string{staging, stable}) + instName := uniq() + volName := uniq() + + r.Run(t, []string{ + "unikraft", "volume", "create", + "--output", "quiet", + "--set", "name=test-" + volName, + "--set", "metro=" + r.Config.MetroName, + "--set", "size=10", + }) + + out := r.Run(t, []string{ + "unikraft", "volume", "inspect", "test-" + volName, + "--output", "template={{ .uuid }}", + }) + srcVolUUID := strings.TrimSpace(out) + assert.NotEmpty(t, srcVolUUID) + + r.Run(t, []string{ + "unikraft", "instance", "create", + "--output", "quiet", + "--set", "name=test-" + instName, + "--set", "metro=" + r.Config.MetroName, + "--set", "image=nginx:latest", + "--set", "autostart=false", + "--set", "resources.memory=128", + "--set", "resources.vcpus=1", + "--set", "volumes=test-" + volName + ":/data", + }) + + out = r.Run(t, []string{ + "unikraft", "instance", "checkpoint", "create", "test-" + instName, + "--set", "wait-timeout=30s", + "--output", "template={{ .name }}", + }) + checkpointName := strings.TrimSpace(out) + assert.NotEmpty(t, checkpointName) + + out = r.Run(t, []string{"unikraft", "instance", "checkpoint", "inspect", checkpointName, "-f", "all"}) + assert.Regexp(t, `at:\s+/data`, out) + + // The checkpoint's volume is a clone, created without a name, so it is + // only addressable by UUID. + out = r.Run(t, []string{ + "unikraft", "instance", "checkpoint", "inspect", checkpointName, + "--output", "template=" + `{{ (index .volumes 0).uuid }}`, + }) + ckptVolUUID := strings.TrimSpace(out) + assert.NotEmpty(t, ckptVolUUID) + assert.NotEqual(t, srcVolUUID, ckptVolUUID) + + out = r.Run(t, []string{"unikraft", "volume", "template", "inspect", ckptVolUUID}) + assert.Regexp(t, `state:\s+template`, out) + r.Run(t, []string{"unikraft", "volume", "inspect", ckptVolUUID}, integ.ExpectFail()) + + // The source volume is unaffected: still a plain volume. + out = r.Run(t, []string{"unikraft", "volume", "inspect", "test-" + volName}) + assert.NotRegexp(t, `state:\s+template`, out) + + r.Run(t, []string{"unikraft", "instance", "checkpoint", "delete", checkpointName}) + r.Run(t, []string{"unikraft", "instance", "delete", "test-" + instName}) + r.Run(t, []string{"unikraft", "--timeout", "30s", "volume", "wait", "--until", "state==available", "test-" + volName}) + r.Run(t, []string{"unikraft", "volume", "delete", "test-" + volName}) + }) + t.Run("create-from-checkpoint", func(t *testing.T) { r := runner(t, true, []string{staging, stable}) baseName := uniq() diff --git a/cmd/unikraft/integration/instance_template_test.go b/cmd/unikraft/integration/instance_template_test.go index 82a1fd46..99e404cf 100644 --- a/cmd/unikraft/integration/instance_template_test.go +++ b/cmd/unikraft/integration/instance_template_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/stretchr/testify/assert" + + integ "unikraft.com/cli/internal/integration" ) func TestInstanceTemplates(t *testing.T) { @@ -85,6 +87,60 @@ func TestInstanceTemplates(t *testing.T) { r.Run(t, []string{"unikraft", "instance", "template", "delete", templateName}) }) + // volumes proves the mountpoints survive the in-place conversion, and that + // the attached volume becomes a volume template - so it is reachable via + // `volume template` and no longer via `volume`. + t.Run("volumes", func(t *testing.T) { + r := runner(t, true, []string{staging, stable}) + instName := uniq() + volName := uniq() + + r.Run(t, []string{ + "unikraft", "volume", "create", + "--output", "quiet", + "--set", "name=test-" + volName, + "--set", "metro=" + r.Config.MetroName, + "--set", "size=10", + }) + + r.Run(t, []string{ + "unikraft", "instance", "create", + "--output", "quiet", + "--set", "name=test-" + instName, + "--set", "metro=" + r.Config.MetroName, + "--set", "image=nginx:latest", + "--set", "autostart=false", + "--set", "resources.memory=128", + "--set", "resources.vcpus=1", + "--set", "volumes=test-" + volName + ":/data", + }) + + out := r.Run(t, []string{ + "unikraft", "instance", "template", "create", "test-" + instName, + "--output", "template={{ .name }}", + }) + templateName := strings.TrimSpace(out) + + out = r.Run(t, []string{"unikraft", "instance", "template", "inspect", templateName, "-f", "all"}) + assert.Contains(t, out, "test-"+volName) + assert.Regexp(t, `at:\s+/data`, out) + + out = r.Run(t, []string{"unikraft", "volume", "template", "list", "--output", "quiet"}) + assert.Contains(t, out, "test-"+volName) + + out = r.Run(t, []string{"unikraft", "volume", "list", "--output", "quiet"}) + assert.NotContains(t, out, "test-"+volName) + + out = r.Run(t, []string{"unikraft", "volume", "template", "inspect", "test-" + volName}) + assert.Regexp(t, `state:\s+template`, out) + r.Run(t, []string{"unikraft", "volume", "inspect", "test-" + volName}, integ.ExpectFail()) + + // Deleting the template takes its volume template with it: the + // conversion re-parented the volume to the template. + r.Run(t, []string{"unikraft", "instance", "template", "delete", templateName}) + r.Run(t, []string{"unikraft", "volume", "template", "delete", "test-" + volName}, integ.AllowFail()) + }) + t.Run("tags", func(t *testing.T) { r := runner(t, true, []string{staging, stable}) instName := uniq() diff --git a/cmd/unikraft/testdata/TestHelp/instances b/cmd/unikraft/testdata/TestHelp/instances index babe06a6..b662f808 100644 --- a/cmd/unikraft/testdata/TestHelp/instances +++ b/cmd/unikraft/testdata/TestHelp/instances @@ -1129,7 +1129,7 @@ Fields: runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, - volumes.*.readonly, volumes.*.size + volumes.*.readonly timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1181,7 +1181,7 @@ Fields: runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, - volumes.*.readonly, volumes.*.size + volumes.*.readonly timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1241,7 +1241,7 @@ Fields: runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, - volumes.*.readonly, volumes.*.size + volumes.*.readonly timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1307,7 +1307,7 @@ Fields: runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, - volumes.*.readonly, volumes.*.size + volumes.*.readonly timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1375,7 +1375,7 @@ Fields: runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, - volumes.*.readonly, volumes.*.size + volumes.*.readonly timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1454,7 +1454,7 @@ Fields: runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, - volumes.*.readonly, volumes.*.size + volumes.*.readonly timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1527,7 +1527,7 @@ Fields: runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, - volumes.*.readonly, volumes.*.size + volumes.*.readonly timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1580,7 +1580,7 @@ Fields: runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, - volumes.*.readonly, volumes.*.size + volumes.*.readonly timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1641,7 +1641,7 @@ Fields: runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, - volumes.*.readonly, volumes.*.size + volumes.*.readonly timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1704,7 +1704,7 @@ Fields: runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, - volumes.*.readonly, volumes.*.size + volumes.*.readonly timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1769,7 +1769,7 @@ Fields: runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, - volumes.*.readonly, volumes.*.size + volumes.*.readonly timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1838,7 +1838,7 @@ Fields: runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, - volumes.*.readonly, volumes.*.size + volumes.*.readonly timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1918,7 +1918,7 @@ Fields: runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, - volumes.*.readonly, volumes.*.size + volumes.*.readonly timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time diff --git a/internal/cmd/instance_checkpoints.go b/internal/cmd/instance_checkpoints.go index 188e3eeb..e86cc8ea 100644 --- a/internal/cmd/instance_checkpoints.go +++ b/internal/cmd/instance_checkpoints.go @@ -96,7 +96,7 @@ type InstanceCheckpoint struct { VCPUs int `mirror:"instance.vcpus" field:"vcpus,short"` } - Volumes []*InstanceVolume `mirror:"instance.volumes" field:",embed"` + Volumes []InstanceTemplateVolume `mirror:"instance.volumes" field:",embed"` Timestamps struct { Created types.RelativeTime `mirror:"instance.created_at" field:",short"` diff --git a/internal/cmd/instance_templates.go b/internal/cmd/instance_templates.go index 4d43beb1..416c6a0a 100644 --- a/internal/cmd/instance_templates.go +++ b/internal/cmd/instance_templates.go @@ -93,7 +93,7 @@ type InstanceTemplate struct { VCPUs int `mirror:"instance.vcpus" field:"vcpus,short"` } - Volumes []*InstanceVolume `mirror:"instance.volumes" field:",embed"` + Volumes []InstanceTemplateVolume `mirror:"instance.volumes" field:",embed"` Timestamps struct { Created types.RelativeTime `mirror:"instance.created_at" field:",short"` diff --git a/internal/cmd/instances.go b/internal/cmd/instances.go index 5ac3351f..d0ac807c 100644 --- a/internal/cmd/instances.go +++ b/internal/cmd/instances.go @@ -298,6 +298,15 @@ func (i *InstanceService) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, (*instanceServiceJSON)(i)) } +// InstanceTemplateVolume is the read-only view of a volume attached to an +// instance template or checkpoint. Converting an instance turns its volumes +// into volume templates, so the link targets that type rather than Volume. +type InstanceTemplateVolume struct { + Link[VolumeTemplate] + At string `name:"at" mirror:"at" json:"at" field:",long"` + Readonly bool `name:"readonly" mirror:"readonly" json:"readonly,omitempty" field:",long"` +} + type InstanceVolume struct { Link[Volume] At string `name:"at" mirror:"at" json:"at" field:",long"` diff --git a/internal/cmd/output_test.go b/internal/cmd/output_test.go index b8cec45e..72f0050a 100644 --- a/internal/cmd/output_test.go +++ b/internal/cmd/output_test.go @@ -182,8 +182,8 @@ func instanceCheckpointsOutputTests(t *testing.T) { Tags: []string{"env-prod", "team-core"}, DeleteLock: true, State: types.InstanceState(platform.InstanceStateCheckpoint), - Volumes: []*cmd.InstanceVolume{ - {Link: cmd.Link[cmd.Volume]{Name: "my-volume"}, At: "/data", Readonly: true}, + Volumes: []cmd.InstanceTemplateVolume{ + {Link: cmd.Link[cmd.VolumeTemplate]{Name: "my-volume"}, At: "/data", Readonly: true}, }, } sample.Runtime.Args = cmd.InstanceArgs{"arg1", "arg2"} diff --git a/internal/cmd/testdata/TestOutput/instance-checkpoints b/internal/cmd/testdata/TestOutput/instance-checkpoints index 10fdeae9..c5d5fa0b 100644 --- a/internal/cmd/testdata/TestOutput/instance-checkpoints +++ b/internal/cmd/testdata/TestOutput/instance-checkpoints @@ -181,18 +181,11 @@ fra my-checkpoint checkpoint nginx ["arg1", "arg2"] 256MiB 2 never "name": "readonly", "value": true, "verbosity": "long" - }, - { - "name": "size", - "verbosity": "invisible", - "create": { - "set": "0B" - } } ], "links": [ { - "type": "volume", + "type": "volume-template", "key": "my-volume" } ], @@ -221,13 +214,6 @@ fra my-checkpoint checkpoint nginx ["arg1", "arg2"] 256MiB 2 never "name": "readonly", "value": false, "verbosity": "long" - }, - { - "name": "size", - "verbosity": "invisible", - "create": { - "set": "0B" - } } ], "verbosity": "long" diff --git a/internal/cmd/testdata/TestOutput/instance-templates b/internal/cmd/testdata/TestOutput/instance-templates index a977574f..00f791bd 100644 --- a/internal/cmd/testdata/TestOutput/instance-templates +++ b/internal/cmd/testdata/TestOutput/instance-templates @@ -156,13 +156,6 @@ fra my-template stopped nginx 128MiB 1 never "name": "readonly", "value": false, "verbosity": "long" - }, - { - "name": "size", - "verbosity": "invisible", - "create": { - "set": "0B" - } } ], "verbosity": "long" From b6028d1f372784e0fc7b1bcfa37d31b67cc117c5 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Tue, 25 Aug 2026 11:28:23 +0100 Subject: [PATCH 3/3] feat(instance-templates): Add type, snapshot and boot timings The status handlers for templates and checkpoints emit add_common, add_basics, add_boot_stats, add_volumes and add_schedops, so type and snapshot were being dropped on the floor for both. Boot and net timings are template-only: the conversion happens in place and keeps the record, whereas a checkpoint is a fresh clone that never boots. Schedules are emitted too, but instances have no schedule support to be consistent with yet, so they are left out. Signed-off-by: Justin Chadwell --- cmd/unikraft/testdata/TestHelp/instances | 32 +++++++++ internal/cmd/instance_checkpoints.go | 5 ++ internal/cmd/instance_templates.go | 10 +++ internal/cmd/output_test.go | 8 ++- .../testdata/TestOutput/instance-checkpoints | 22 ++++++ .../testdata/TestOutput/instance-templates | 67 +++++++++++++++---- 6 files changed, 131 insertions(+), 13 deletions(-) diff --git a/cmd/unikraft/testdata/TestHelp/instances b/cmd/unikraft/testdata/TestHelp/instances index b662f808..f78d7153 100644 --- a/cmd/unikraft/testdata/TestHelp/instances +++ b/cmd/unikraft/testdata/TestHelp/instances @@ -1126,11 +1126,14 @@ Fields: delete-lock state image + type runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, volumes.*.readonly + snapshot, snapshot.uuid timestamps, timestamps.created + timing, timing.boot-time, timing.net-time scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time restart, restart.policy @@ -1178,11 +1181,14 @@ Fields: delete-lock state image + type runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, volumes.*.readonly + snapshot, snapshot.uuid timestamps, timestamps.created + timing, timing.boot-time, timing.net-time scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time restart, restart.policy @@ -1238,11 +1244,14 @@ Fields: delete-lock state image + type runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, volumes.*.readonly + snapshot, snapshot.uuid timestamps, timestamps.created + timing, timing.boot-time, timing.net-time scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time restart, restart.policy @@ -1304,11 +1313,14 @@ Fields: delete-lock state image + type runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, volumes.*.readonly + snapshot, snapshot.uuid timestamps, timestamps.created + timing, timing.boot-time, timing.net-time scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time restart, restart.policy @@ -1372,11 +1384,14 @@ Fields: delete-lock state image + type runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, volumes.*.readonly + snapshot, snapshot.uuid timestamps, timestamps.created + timing, timing.boot-time, timing.net-time scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time restart, restart.policy @@ -1451,11 +1466,14 @@ Fields: delete-lock state image + type runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, volumes.*.readonly + snapshot, snapshot.uuid timestamps, timestamps.created + timing, timing.boot-time, timing.net-time scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time restart, restart.policy @@ -1524,10 +1542,12 @@ Fields: delete-lock state image + type runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, volumes.*.readonly + snapshot, snapshot.uuid timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1577,10 +1597,12 @@ Fields: delete-lock state image + type runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, volumes.*.readonly + snapshot, snapshot.uuid timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1638,10 +1660,12 @@ Fields: delete-lock state image + type runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, volumes.*.readonly + snapshot, snapshot.uuid timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1701,10 +1725,12 @@ Fields: delete-lock state image + type runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, volumes.*.readonly + snapshot, snapshot.uuid timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1766,10 +1792,12 @@ Fields: delete-lock state image + type runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, volumes.*.readonly + snapshot, snapshot.uuid timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1835,10 +1863,12 @@ Fields: delete-lock state image + type runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, volumes.*.readonly + snapshot, snapshot.uuid timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time @@ -1915,10 +1945,12 @@ Fields: delete-lock state image + type runtime, runtime.args, runtime.env resources, resources.memory, resources.vcpus volumes, volumes.*, volumes.*.name, volumes.*.uuid, volumes.*.at, volumes.*.readonly + snapshot, snapshot.uuid timestamps, timestamps.created scale-to-zero, scale-to-zero.enabled, scale-to-zero.policy, scale-to- zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time diff --git a/internal/cmd/instance_checkpoints.go b/internal/cmd/instance_checkpoints.go index e86cc8ea..d47f38c0 100644 --- a/internal/cmd/instance_checkpoints.go +++ b/internal/cmd/instance_checkpoints.go @@ -85,6 +85,7 @@ type InstanceCheckpoint struct { State types.InstanceState `mirror:"instance.state" field:",short"` Image types.ImageRef[reference.Named] `mirror:"instance.image" field:",short"` + Type_ *platform.InstanceType `mirror:"instance.type" field:"type,long"` Runtime struct { Args InstanceArgs `mirror:"instance.args" field:",short"` @@ -98,6 +99,10 @@ type InstanceCheckpoint struct { Volumes []InstanceTemplateVolume `mirror:"instance.volumes" field:",embed"` + Snapshot struct { + UUID string `mirror:"instance.snapshot.uuid" field:",long"` + } + Timestamps struct { Created types.RelativeTime `mirror:"instance.created_at" field:",short"` } diff --git a/internal/cmd/instance_templates.go b/internal/cmd/instance_templates.go index 416c6a0a..3a98230f 100644 --- a/internal/cmd/instance_templates.go +++ b/internal/cmd/instance_templates.go @@ -82,6 +82,7 @@ type InstanceTemplate struct { State types.InstanceState `mirror:"instance.state" field:",short"` Image types.ImageRef[reference.Named] `mirror:"instance.image" field:",short"` + Type_ *platform.InstanceType `mirror:"instance.type" field:"type,long"` Runtime struct { Args InstanceArgs `mirror:"instance.args" field:",short"` @@ -95,10 +96,19 @@ type InstanceTemplate struct { Volumes []InstanceTemplateVolume `mirror:"instance.volumes" field:",embed"` + Snapshot struct { + UUID string `mirror:"instance.snapshot.uuid" field:",long"` + } + Timestamps struct { Created types.RelativeTime `mirror:"instance.created_at" field:",short"` } + Timing struct { + BootTime types.DurationUS `mirror:"instance.boot_time_us" field:",long"` + NetTime types.DurationUS `mirror:"instance.net_time_us"` + } + ScaleToZero InstanceScaleToZero `field:",embed" mirror:"instance.scale_to_zero"` Restart struct { diff --git a/internal/cmd/output_test.go b/internal/cmd/output_test.go index 72f0050a..c9036917 100644 --- a/internal/cmd/output_test.go +++ b/internal/cmd/output_test.go @@ -165,10 +165,14 @@ func instanceTemplatesOutputTests(t *testing.T) { Name: "my-template", UUID: "d4e5f6a7-b8c9-0123-def0-123456789abc", Tags: []string{"env-staging"}, - State: types.InstanceState(platform.InstanceStateStopped), + State: types.InstanceState(platform.InstanceStateTemplate), + Type_: new(platform.InstanceTypeMicro), } sample.Resources.Memory = 128 sample.Resources.VCPUs = 1 + sample.Snapshot.UUID = "1b2c3d4e-5f6a-7890-bcde-f1234567890a" + sample.Timing.BootTime = 12000 + sample.Timing.NetTime = 34000 require.NoError(t, sample.Image.UnmarshalText([]byte("nginx:latest"))) integ.Gild[resource.Resource](t, dumpResource, sample) @@ -182,6 +186,7 @@ func instanceCheckpointsOutputTests(t *testing.T) { Tags: []string{"env-prod", "team-core"}, DeleteLock: true, State: types.InstanceState(platform.InstanceStateCheckpoint), + Type_: new(platform.InstanceTypeMicro), Volumes: []cmd.InstanceTemplateVolume{ {Link: cmd.Link[cmd.VolumeTemplate]{Name: "my-volume"}, At: "/data", Readonly: true}, }, @@ -191,6 +196,7 @@ func instanceCheckpointsOutputTests(t *testing.T) { sample.Resources.Memory = 256 sample.Resources.VCPUs = 2 sample.Restart.Policy = "always" + sample.Snapshot.UUID = "2c3d4e5f-6a7b-8901-cdef-1234567890ab" require.NoError(t, sample.Image.UnmarshalText([]byte("nginx:latest"))) integ.Gild[resource.Resource](t, dumpResource, sample) diff --git a/internal/cmd/testdata/TestOutput/instance-checkpoints b/internal/cmd/testdata/TestOutput/instance-checkpoints index c5d5fa0b..d36f8c68 100644 --- a/internal/cmd/testdata/TestOutput/instance-checkpoints +++ b/internal/cmd/testdata/TestOutput/instance-checkpoints @@ -6,6 +6,7 @@ tags: ["env-prod", "team-core"] delete-lock: true state: checkpoint image: nginx +type: micro runtime: args: ["arg1", "arg2"] env: @@ -18,6 +19,8 @@ volumes: - name: my-volume at: /data readonly: true +snapshot: + uuid: 2c3d4e5f-6a7b-8901-cdef-1234567890ab ==================================== kv-all ==================================== metro: fra @@ -27,6 +30,7 @@ tags: ["env-prod", "team-core"] delete-lock: true state: checkpoint image: nginx +type: micro runtime: args: ["arg1", "arg2"] env: @@ -40,6 +44,8 @@ volumes: uuid: at: /data readonly: true +snapshot: + uuid: 2c3d4e5f-6a7b-8901-cdef-1234567890ab timestamps: created: never scale-to-zero: @@ -118,6 +124,11 @@ fra my-checkpoint checkpoint nginx ["arg1", "arg2"] 256MiB 2 never ], "verbosity": "short" }, + { + "name": "type", + "value": "micro", + "verbosity": "long" + }, { "name": "runtime", "subfields": [ @@ -220,6 +231,17 @@ fra my-checkpoint checkpoint nginx ["arg1", "arg2"] 256MiB 2 never }, "verbosity": "long" }, + { + "name": "snapshot", + "subfields": [ + { + "name": "uuid", + "value": "2c3d4e5f-6a7b-8901-cdef-1234567890ab", + "verbosity": "long" + } + ], + "verbosity": "long" + }, { "name": "timestamps", "subfields": [ diff --git a/internal/cmd/testdata/TestOutput/instance-templates b/internal/cmd/testdata/TestOutput/instance-templates index 00f791bd..1d629ef8 100644 --- a/internal/cmd/testdata/TestOutput/instance-templates +++ b/internal/cmd/testdata/TestOutput/instance-templates @@ -1,13 +1,18 @@ ====================================== kv ====================================== -metro: fra -name: my-template -uuid: d4e5f6a7-b8c9-0123-def0-123456789abc -tags: ["env-staging"] -state: stopped -image: nginx +metro: fra +name: my-template +uuid: d4e5f6a7-b8c9-0123-def0-123456789abc +tags: ["env-staging"] +state: template +image: nginx +type: micro resources: - memory: 128MiB - vcpus: 1 + memory: 128MiB + vcpus: 1 +snapshot: + uuid: 1b2c3d4e-5f6a-7890-bcde-f1234567890a +timing: + boot-time: 12ms ==================================== kv-all ==================================== metro: fra @@ -15,8 +20,9 @@ name: my-template uuid: d4e5f6a7-b8c9-0123-def0-123456789abc tags: ["env-staging"] delete-lock: false -state: stopped +state: template image: nginx +type: micro runtime: args: env: @@ -24,8 +30,13 @@ resources: memory: 128MiB vcpus: 1 volumes: +snapshot: + uuid: 1b2c3d4e-5f6a-7890-bcde-f1234567890a timestamps: created: never +timing: + boot-time: 12ms + net-time: 34ms scale-to-zero: enabled: false policy: @@ -36,8 +47,8 @@ restart: policy: ==================================== table ===================================== -METRO NAME STATE IMAGE ARGS MEMORY VCPUS CREATED -fra my-template stopped nginx 128MiB 1 never +METRO NAME STATE IMAGE ARGS MEMORY VCPUS CREATED +fra my-template template nginx 128MiB 1 never ==================================== debug ===================================== [ @@ -86,7 +97,7 @@ fra my-template stopped nginx 128MiB 1 never }, { "name": "state", - "value": "stopped", + "value": "template", "verbosity": "short" }, { @@ -100,6 +111,11 @@ fra my-template stopped nginx 128MiB 1 never ], "verbosity": "short" }, + { + "name": "type", + "value": "micro", + "verbosity": "long" + }, { "name": "runtime", "subfields": [ @@ -162,6 +178,17 @@ fra my-template stopped nginx 128MiB 1 never }, "verbosity": "long" }, + { + "name": "snapshot", + "subfields": [ + { + "name": "uuid", + "value": "1b2c3d4e-5f6a-7890-bcde-f1234567890a", + "verbosity": "long" + } + ], + "verbosity": "long" + }, { "name": "timestamps", "subfields": [ @@ -173,6 +200,22 @@ fra my-template stopped nginx 128MiB 1 never ], "verbosity": "short" }, + { + "name": "timing", + "subfields": [ + { + "name": "boot-time", + "value": "12ms", + "verbosity": "long" + }, + { + "name": "net-time", + "value": "34ms", + "verbosity": "hidden" + } + ], + "verbosity": "long" + }, { "name": "scale-to-zero", "subfields": [