Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ jobs:
EXOSCALE_ZONE: ch-gva-2
run: |
cd tests/e2e
go test -v -tags=api -timeout 30m -run TestScriptsAPI
go test -v -tags=api -timeout 30m -run TestScriptsAPI 2>&1
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is stderr redirection needed now? Put at least a note about it in the PR description so it ends up in the commit message when we merge.

Copy link
Copy Markdown
Contributor Author

@natalie-o-perret natalie-o-perret Mar 5, 2026

Choose a reason for hiding this comment

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

It's a tiny detail, GitHub Action that shows an error cause of the presence of a stderr (when looking at the details, the job isn't in error, though just when you're about to expand the job output / details)

4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

- `exo compute eip list` now shows the description & the management type of the EIP #803
- sks: add `rotate-karpenter-credentials` command #797

### Improvements

- compute instance: enrich "not found" error with the zone searched and a hint to use -z #805
- sks: add `active-nodepool-templates` command #797

### Bug fixes
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_console_url.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (c *instanceConsoleURLCmd) CmdRun(cmd *cobra.Command, _ []string) error {
return err
}

foundInstance, err := resp.FindListInstancesResponseInstances(c.Instance)
foundInstance, err := findInstance(resp, c.Instance, string(c.Zone))
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (c *instanceDeleteCmd) CmdRun(_ *cobra.Command, _ []string) error {

instanceToDelete := []v3.UUID{}
for _, i := range c.Instances {
instance, err := instances.FindListInstancesResponseInstances(i)
instance, err := findInstance(instances, i, c.Zone)
if err != nil {
if !c.Force {
return err
Expand Down
4 changes: 2 additions & 2 deletions cmd/compute/instance/instance_elastic_ip_attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ func (c *instanceEIPAttachCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instancesList.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instancesList, c.Instance, c.Zone)
if err != nil {
return fmt.Errorf("error retrieving Instance: %w", err)
return err
}

elasticIPs, err := client.ListElasticIPS(ctx)
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_elastic_ip_detach.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (c *instanceEIPDetachCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instancesList.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instancesList, c.Instance, c.Zone)
if err != nil {
return fmt.Errorf("error retrieving Instance: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_enable_tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (c *instanceEnableTPMCmd) CmdRun(_ *cobra.Command, _ []string) error {
return err
}

instance, err := resp.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(resp, c.Instance, string(c.Zone))
if err != nil {
return err
}
Expand Down
26 changes: 26 additions & 0 deletions cmd/compute/instance/instance_find.go
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The file naming does not fit in our code organization (file name -> command name).
Put this function in the instance.go which is logically shared by all commands.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package instance

import (
"errors"
"fmt"

v3 "github.com/exoscale/egoscale/v3"
)

// findInstance looks up an instance by name or ID from a ListInstancesResponse
// and enriches the "not found" error with the zone that was searched,
// reminding the user to use -z to target a different zone.
func findInstance(resp *v3.ListInstancesResponse, nameOrID, zone string) (v3.ListInstancesResponseInstances, error) {
instance, err := resp.FindListInstancesResponseInstances(nameOrID)
if err != nil {
if errors.Is(err, v3.ErrNotFound) {
return v3.ListInstancesResponseInstances{}, fmt.Errorf(
"instance %q not found in zone %s\nHint: use -z <zone> to specify a different zone, or run 'exo compute instance list' to see instances across all zones",
nameOrID,
zone,
)
}
return v3.ListInstancesResponseInstances{}, err
}
return instance, nil
}
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_private_network_attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (c *instancePrivnetAttachCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_private_network_detach.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (c *instancePrivnetDetachCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_private_network_updateip.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (c *instancePrivnetUpdateIPCmd) CmdRun(_ *cobra.Command, _ []string) error
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_reboot.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (c *instanceRebootCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (c *instanceResetCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_reset_password.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (c *instanceResetPasswordCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_resizedisk.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (c *instanceResizeDiskCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_reveal_password.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (c *instanceRevealCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_scale.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (c *instanceScaleCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_scp.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (c *instanceSCPCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_security_group_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (c *instanceSGAddCmd) CmdRun(cmd *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_security_group_remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (c *instanceSGRemoveCmd) CmdRun(cmd *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_show.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (c *instanceShowCmd) CmdRun(cmd *cobra.Command, _ []string) error {
return err
}

foundInstance, err := resp.FindListInstancesResponseInstances(c.Instance)
foundInstance, err := findInstance(resp, c.Instance, string(c.Zone))
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_snapshot_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (c *instanceSnapshotCreateCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_snapshot_revert.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (c *instanceSnapshotRevertCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (c *instanceSSHCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_start.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (c *instanceStartCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (c *instanceStopCmd) CmdRun(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/compute/instance/instance_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (c *instanceUpdateCmd) CmdRun(cmd *cobra.Command, _ []string) error {
if err != nil {
return err
}
instance, err := instances.FindListInstancesResponseInstances(c.Instance)
instance, err := findInstance(instances, c.Instance, c.Zone)
if err != nil {
return err
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Test: exo compute instance <action> returns enriched error when instance not found
# Verifies that when an instance lookup fails, the error message includes the zone
# that was searched and a hint to use -z.
# No resources are created so no teardown is required.
# TEST_ZONE is injected by the API test runner.

# show: instance not found should mention zone and hint
! exec exo --output-format json compute instance show nonexistent-e2e-instance
stderr 'not found in zone'
stderr 'Hint: use -z'

# reboot: same helper, same enriched error
! exec exo --output-format json compute instance reboot --force nonexistent-e2e-instance
stderr 'not found in zone'
stderr 'Hint: use -z'

# stop: same helper, same enriched error
! exec exo --output-format json compute instance stop --force nonexistent-e2e-instance
stderr 'not found in zone'
stderr 'Hint: use -z'
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Test: all 24 instance subcommands using findInstance produce enriched not-found errors
# Companion to instance_not_found_error.txtar which covers show/reboot/stop.
# This scenario exercises the remaining 21 commands to confirm that every
# one of them surfaces the zone and the -z hint when the instance does not exist.
# No resources are created so no teardown is required.

# --- Actions that take only an instance name (+ optional --force) ---

# start
! exec exo compute instance start --force nonexistent-e2e-instance
stderr 'not found in zone'
stderr 'Hint: use -z'

# delete (without --force: findInstance error is returned directly;
# with --force the command prints a warning and exits 0 by design)
! exec exo compute instance delete nonexistent-e2e-instance
stderr 'not found in zone'
stderr 'Hint: use -z'

# update
! exec exo compute instance update nonexistent-e2e-instance --name dummy
stderr 'not found in zone'
stderr 'Hint: use -z'

# reset
! exec exo compute instance reset --force nonexistent-e2e-instance
stderr 'not found in zone'
stderr 'Hint: use -z'

# reset-password
! exec exo compute instance reset-password nonexistent-e2e-instance
stderr 'not found in zone'
stderr 'Hint: use -z'

# reveal-password
! exec exo compute instance reveal-password nonexistent-e2e-instance
stderr 'not found in zone'
stderr 'Hint: use -z'

# console-url
! exec exo compute instance console-url nonexistent-e2e-instance
stderr 'not found in zone'
stderr 'Hint: use -z'

# enable-tpm
! exec exo compute instance enable-tpm --force nonexistent-e2e-instance
stderr 'not found in zone'
stderr 'Hint: use -z'

# snapshot create
! exec exo compute instance snapshot create nonexistent-e2e-instance
stderr 'not found in zone'
stderr 'Hint: use -z'

# --- Actions that take an instance name + a second positional argument ---

# scale (requires instance + type)
! exec exo compute instance scale --force nonexistent-e2e-instance standard.small
stderr 'not found in zone'
stderr 'Hint: use -z'

# resize-disk (requires instance + size)
! exec exo compute instance resize-disk --force nonexistent-e2e-instance 20
stderr 'not found in zone'
stderr 'Hint: use -z'

# snapshot revert (requires instance + snapshot-id)
! exec exo compute instance snapshot revert --force nonexistent-e2e-instance 00000000-0000-0000-0000-000000000000
stderr 'not found in zone'
stderr 'Hint: use -z'

# security-group add (requires instance + sg name)
! exec exo compute instance security-group add nonexistent-e2e-instance default
stderr 'not found in zone'
stderr 'Hint: use -z'

# security-group remove (requires instance + sg name)
! exec exo compute instance security-group remove nonexistent-e2e-instance default
stderr 'not found in zone'
stderr 'Hint: use -z'

# private-network attach (requires instance + pn name)
! exec exo compute instance private-network attach nonexistent-e2e-instance dummy-pn
stderr 'not found in zone'
stderr 'Hint: use -z'

# private-network detach (requires instance + pn name)
! exec exo compute instance private-network detach nonexistent-e2e-instance dummy-pn
stderr 'not found in zone'
stderr 'Hint: use -z'

# private-network update-ip (requires instance + pn name)
! exec exo compute instance private-network update-ip nonexistent-e2e-instance dummy-pn
stderr 'not found in zone'
stderr 'Hint: use -z'

# elastic-ip attach (requires instance + eip)
! exec exo compute instance elastic-ip attach nonexistent-e2e-instance 1.2.3.4
stderr 'not found in zone'
stderr 'Hint: use -z'

# elastic-ip detach (requires instance + eip)
! exec exo compute instance elastic-ip detach nonexistent-e2e-instance 1.2.3.4
stderr 'not found in zone'
stderr 'Hint: use -z'

# ssh (requires instance; will fail at instance lookup before attempting SSH)
! exec exo compute instance ssh nonexistent-e2e-instance
stderr 'not found in zone'
stderr 'Hint: use -z'

# scp (requires instance + source + target)
! exec exo compute instance scp nonexistent-e2e-instance /dev/null /tmp/dummy
stderr 'not found in zone'
stderr 'Hint: use -z'