Skip to content
Draft
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
27 changes: 27 additions & 0 deletions cmd/unikraft/instances_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,33 @@ func instancesTests(t *testing.T, r *testRunner) {
})
})

t.Run("rm", func(t *testing.T) {
r.
online().
withCleaners(instanceCleaners).
run(t, []command{
// Create a running instance with --rm so it is auto-deleted
// when stopped.
{args: []string{
unikraftCmd, "instance", "create",
"--output", "quiet",
"--rm",
"--set", "name=test-$UNIQ_INST",
"--set", "metro=" + metroName,
"--set", "image=nginx:latest",
"--set", "autostart=true",
"--set", "resources.memory=128",
"--set", "resources.vcpus=1",
}},
{args: []string{unikraftCmd, "instance", "wait", "--until", "state==running", "--timeout", "30s", "test-$UNIQ_INST"}},
// Stop the instance — delete-on-stop removes it and the
// diff should show everything being deleted.
{args: []string{unikraftCmd, "instance", "stop", "test-$UNIQ_INST"}},
// Verify the instance no longer exists.
{args: []string{unikraftCmd, "instance", "inspect", "test-$UNIQ_INST"}, allowErr: true},
})
})

t.Run("add-domain", func(t *testing.T) {
r.
online().
Expand Down
2 changes: 2 additions & 0 deletions cmd/unikraft/testdata/TestGolden/instances/help

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 50 additions & 0 deletions cmd/unikraft/testdata/TestGolden/instances/rm

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 30 additions & 5 deletions internal/cmd/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,16 @@ type InstanceCreateCmd struct {
Replicas int64 `group:"flag-create" shortcut:"replicas" help:"Number of replicas." placeholder:"n" example:"1,3"`
Features []string `group:"flag-create" shortcut:"features" help:"Instance features." placeholder:"feature"`
Template string `group:"flag-create" shortcut:"template" help:"Create from instance template." placeholder:"name"`
Rm bool `group:"flag-create" help:"Automatically delete the instance when it stops."`
}

func (c *InstanceCreateCmd) Run(ctx context.Context, stdio config.Stdio, sandbox *resource.Sandbox, kctx *kong.Context) error {
if err := cmd.ApplyShortcutFlags(&c.SetArgs, kctx.Flags()); err != nil {
return err
}
if c.Rm {
c.Set = append(c.Set, map[string]string{"features": string(platform.CreateInstanceRequestFeaturesDelete_on_stop)})
}
return c.ResourceCreateCmd.Run(ctx, stdio, sandbox)
}

Expand Down Expand Up @@ -1183,9 +1187,17 @@ func (c *InstancesStopCmd) Run(ctx context.Context, stdio config.Stdio) error {
}

updated, getErr := Instance{}.Get(ctx, stopped.Strings())
opErr = errors.Join(opErr, getErr)
// If the stopped instances are not found (e.g. removed by
// delete-on-stop), show a diff against an empty "after" state
// instead of returning an error.
if getErr != nil && len(updated) == 0 {
return opErr
var refErr group.ErrRefNotFound
if !errors.As(getErr, &refErr) {
opErr = errors.Join(opErr, getErr)
return opErr
}
} else {
opErr = errors.Join(opErr, getErr)
}

keySet := make(map[string]struct{}, len(stopped))
Expand Down Expand Up @@ -1252,14 +1264,27 @@ func (c *InstancesRestartCmd) Run(ctx context.Context, stdio config.Stdio) error

started, startErr := startInstances(ctx, g, stopped)
opErr = errors.Join(opErr, startErr)
// If all stopped instances were removed (e.g. by delete-on-stop)
// before they could be restarted, show a diff reflecting the
// deletion instead of returning a confusing error.
if len(started) == 0 {
return opErr
var refErr group.ErrRefNotFound
if startErr == nil || !errors.As(startErr, &refErr) {
return opErr
}
}
Comment on lines +1267 to 1275
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

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

The new delete-on-stop tolerance path in InstancesRestartCmd.Run (handling group.ErrRefNotFound when startInstances/Get can’t find instances) isn’t covered by the golden CLI tests. Adding a test similar to the new rm stop test but exercising unikraft instance restart --rm (or restarting an instance created with --rm) would help prevent regressions.

Copilot uses AI. Check for mistakes.

updated, getErr := Instance{}.Get(ctx, started.Strings())
opErr = errors.Join(opErr, getErr)
// Same as above: tolerate not-found when delete-on-stop already
// removed the instances.
if getErr != nil && len(updated) == 0 {
return opErr
var refErr group.ErrRefNotFound
if !errors.As(getErr, &refErr) {
opErr = errors.Join(opErr, getErr)
return opErr
}
} else {
opErr = errors.Join(opErr, getErr)
}

keySet := make(map[string]struct{}, len(started))
Expand Down
Loading