From 970d985d1ae82b9273f55bf51614607ff274fca1 Mon Sep 17 00:00:00 2001 From: Coker Richard <82083946+cokerrd@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:24:23 +0100 Subject: [PATCH] feat: add user feedback for volume attach/detach commands --- internal/commands/volume.go | 21 ++++++--------------- pkg/api/volume/volume.go | 8 ++++---- pkg/api/volume/volume_test.go | 22 ++++++++++++++-------- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/internal/commands/volume.go b/internal/commands/volume.go index 9768a03..9a90f70 100644 --- a/internal/commands/volume.go +++ b/internal/commands/volume.go @@ -206,18 +206,13 @@ func newVolumeAttachCmd() *cobra.Command { ctx, cancel := context.WithTimeout(context.Background(), time.Duration(getTimeout(cmd))*time.Second) defer cancel() - vol, err := svc.Attach(ctx, volumeSlug, vmSlug) + resp, err := svc.Attach(ctx, volumeSlug, vmSlug) if err != nil { return fmt.Errorf("volume attach: %w", err) } - headers := []string{"SLUG", "NAME", "SIZE", "VM ID"} - rows := [][]string{{ - vol.Slug, - vol.Name, - formatSize(vol.Size), - vol.VirtualMachineID, - }} + headers := []string{"STATUS", "MESSAGE"} + rows := [][]string{{resp.Status, resp.Message}} return printer.PrintTable(headers, rows) }, } @@ -241,7 +236,7 @@ func newVolumeDetachCmd() *cobra.Command { ctx, cancel := context.WithTimeout(context.Background(), time.Duration(getTimeout(cmd))*time.Second) defer cancel() - vol, err := svc.Detach(ctx, volumeSlug) + resp, err := svc.Detach(ctx, volumeSlug) if err != nil { if apierrors.IsResourceNotFound(err) { fmt.Fprintf(os.Stderr, "Volume %q not found — already detached or deleted.\n", volumeSlug) @@ -250,12 +245,8 @@ func newVolumeDetachCmd() *cobra.Command { return fmt.Errorf("volume detach: %w", err) } - headers := []string{"SLUG", "NAME", "SIZE"} - rows := [][]string{{ - vol.Slug, - vol.Name, - formatSize(vol.Size), - }} + headers := []string{"STATUS", "MESSAGE"} + rows := [][]string{{resp.Status, resp.Message}} return printer.PrintTable(headers, rows) }, } diff --git a/pkg/api/volume/volume.go b/pkg/api/volume/volume.go index 63e99eb..c256798 100644 --- a/pkg/api/volume/volume.go +++ b/pkg/api/volume/volume.go @@ -172,24 +172,24 @@ func (s *Service) Create(ctx context.Context, req CreateRequest) (*Volume, error } // Attach attaches a volume to a virtual machine. -func (s *Service) Attach(ctx context.Context, volumeSlug, vmSlug string) (*Volume, error) { +func (s *Service) Attach(ctx context.Context, volumeSlug, vmSlug string) (*singleResponse, error) { body := AttachRequest{VirtualMachine: vmSlug} var resp singleResponse path := fmt.Sprintf("/blockstorages/%s/attach", volumeSlug) if err := s.client.Post(ctx, path, body, &resp); err != nil { return nil, fmt.Errorf("attaching block storage %s to VM %s: %w", volumeSlug, vmSlug, err) } - return &resp.Data, nil + return &resp, nil } // Detach detaches a volume from its virtual machine. -func (s *Service) Detach(ctx context.Context, volumeSlug string) (*Volume, error) { +func (s *Service) Detach(ctx context.Context, volumeSlug string) (*singleResponse, error) { var resp singleResponse path := fmt.Sprintf("/blockstorages/%s/detach", volumeSlug) if err := s.client.Post(ctx, path, nil, &resp); err != nil { return nil, fmt.Errorf("detaching block storage %s: %w", volumeSlug, err) } - return &resp.Data, nil + return &resp, nil } // Delete permanently deletes a block storage volume. The volume must be detached first. diff --git a/pkg/api/volume/volume_test.go b/pkg/api/volume/volume_test.go index 5a5e41b..63ada81 100644 --- a/pkg/api/volume/volume_test.go +++ b/pkg/api/volume/volume_test.go @@ -142,12 +142,12 @@ func TestVolumeAttach(t *testing.T) { json.NewDecoder(r.Body).Decode(&gotBody) result := volume.Volume{ID: "vol-1", Slug: "root-4153", VirtualMachineID: "vm-1"} w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(singleResponse{Status: "Success", Message: "Ok", Data: result}) + json.NewEncoder(w).Encode(singleResponse{Status: "Success", Message: "Attaching block storage.", Data: result}) })) defer srv.Close() svc := volume.NewService(newTestClient(t, srv)) - vol, err := svc.Attach(context.Background(), "root-4153", "test-vm-1") + resp, err := svc.Attach(context.Background(), "root-4153", "test-vm-1") if err != nil { t.Fatalf("Attach() error = %v", err) } @@ -157,8 +157,11 @@ func TestVolumeAttach(t *testing.T) { if gotBody["virtual_machine"] != "test-vm-1" { t.Errorf("body virtual_machine = %v, want %q", gotBody["virtual_machine"], "test-vm-1") } - if vol.VirtualMachineID != "vm-1" { - t.Errorf("vol.VirtualMachineID = %q, want %q", vol.VirtualMachineID, "vm-1") + if resp.Message != "Attaching block storage." { + t.Errorf("resp.Message = %q, want %q", resp.Message, "Attaching block storage.") + } + if resp.Status != "Success" { + t.Errorf("resp.Status = %q, want %q", resp.Status, "Success") } } @@ -169,12 +172,12 @@ func TestVolumeDetach(t *testing.T) { gotPath = r.URL.Path result := volume.Volume{ID: "vol-1", Slug: "root-4153"} w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(singleResponse{Status: "Success", Message: "Ok", Data: result}) + json.NewEncoder(w).Encode(singleResponse{Status: "Success", Message: "Detaching block storage.", Data: result}) })) defer srv.Close() svc := volume.NewService(newTestClient(t, srv)) - vol, err := svc.Detach(context.Background(), "root-4153") + resp, err := svc.Detach(context.Background(), "root-4153") if err != nil { t.Fatalf("Detach() error = %v", err) } @@ -184,8 +187,11 @@ func TestVolumeDetach(t *testing.T) { if gotPath != "/blockstorages/root-4153/detach" { t.Errorf("path = %q, want %q", gotPath, "/blockstorages/root-4153/detach") } - if vol.Slug != "root-4153" { - t.Errorf("vol.Slug = %q, want %q", vol.Slug, "root-4153") + if resp.Status != "Success" { + t.Errorf("resp.Status = %q, want %q", resp.Status, "Success") + } + if resp.Message != "Detaching block storage." { + t.Errorf("resp.Message = %q, want %q", resp.Message, "Detaching block storage.") } }