cloud/shepherd/equinix/cli: Rename servers before deletion

This avoids collisions when redeploying servers

Change-Id: I94c8c5e0b1b601ee6513f7baddb251053ce9430b
Reviewed-on: https://review.monogon.dev/c/monogon/+/1864
Tested-by: Jenkins CI
Reviewed-by: Leopold Schabel <leo@monogon.tech>
diff --git a/cloud/shepherd/equinix/cli/cmd_delete.go b/cloud/shepherd/equinix/cli/cmd_delete.go
index e3c51a5..07b4dc7 100644
--- a/cloud/shepherd/equinix/cli/cmd_delete.go
+++ b/cloud/shepherd/equinix/cli/cmd_delete.go
@@ -4,6 +4,7 @@
 	"context"
 	"time"
 
+	"github.com/packethost/packngo"
 	"github.com/spf13/cobra"
 	"k8s.io/klog/v2"
 
@@ -42,6 +43,15 @@
 	time.Sleep(5 * time.Second)
 
 	for _, d := range devices {
+		h := "deleted-" + d.Hostname
+		_, err := api.UpdateDevice(ctx, d.ID, &packngo.DeviceUpdateRequest{
+			Hostname: &h,
+		})
+		if err != nil {
+			klog.Infof("failed updating device %s (%s): %v", d.ID, d.Hostname, err)
+			continue
+		}
+
 		klog.Infof("deleting %s (%s)...", d.ID, d.Hostname)
 		if err := api.DeleteDevice(ctx, d.ID); err != nil {
 			klog.Infof("failed deleting device %s (%s): %v", d.ID, d.Hostname, err)
diff --git a/cloud/shepherd/equinix/manager/fakequinix_test.go b/cloud/shepherd/equinix/manager/fakequinix_test.go
index e3a04d5..4c0ca60 100644
--- a/cloud/shepherd/equinix/manager/fakequinix_test.go
+++ b/cloud/shepherd/equinix/manager/fakequinix_test.go
@@ -2,6 +2,7 @@
 
 import (
 	"context"
+	"fmt"
 	"net/http"
 	"sync"
 
@@ -78,6 +79,10 @@
 	return res, nil
 }
 
+func (f *fakequinix) UpdateDevice(ctx context.Context, id string, r *packngo.DeviceUpdateRequest) (*packngo.Device, error) {
+	return nil, fmt.Errorf("not implemented")
+}
+
 // MoveReservation is not implemented in fakequinix
 func (f *fakequinix) MoveReservation(_ context.Context, hardwareReservationDID, projectID string) (*packngo.HardwareReservation, error) {
 	return nil, &packngo.ErrorResponse{
diff --git a/cloud/shepherd/equinix/wrapngo/wrapn.go b/cloud/shepherd/equinix/wrapngo/wrapn.go
index d193b1a..7bd4522 100644
--- a/cloud/shepherd/equinix/wrapngo/wrapn.go
+++ b/cloud/shepherd/equinix/wrapngo/wrapn.go
@@ -101,6 +101,8 @@
 	// retry was needed but in the meantime the requested hardware reservation from
 	// which this machine was requested got lost.
 	CreateDevice(ctx context.Context, request *packngo.DeviceCreateRequest) (*packngo.Device, error)
+
+	UpdateDevice(ctx context.Context, id string, request *packngo.DeviceUpdateRequest) (*packngo.Device, error)
 	RebootDevice(ctx context.Context, did string) error
 	DeleteDevice(ctx context.Context, id string) error
 
@@ -301,6 +303,13 @@
 	})
 }
 
+func (e *client) UpdateDevice(ctx context.Context, id string, r *packngo.DeviceUpdateRequest) (*packngo.Device, error) {
+	return wrap(ctx, e, func(cl *packngo.Client) (*packngo.Device, error) {
+		dev, _, err := cl.Devices.Update(id, r)
+		return dev, err
+	})
+}
+
 func (e *client) ListDevices(ctx context.Context, pid string) ([]packngo.Device, error) {
 	return wrap(ctx, e, func(cl *packngo.Client) ([]packngo.Device, error) {
 		// to increase the chances of a stable pagination, we sort the devices by hostname
@@ -309,16 +318,6 @@
 	})
 }
 
-func (e *client) UpdateDevice(ctx context.Context, id string, r *packngo.DeviceUpdateRequest) (*packngo.Device, error) {
-	return wrap(ctx, e, func(p *packngo.Client) (*packngo.Device, error) {
-		d, _, err := p.Devices.Update(id, r)
-		if err != nil {
-			return nil, fmt.Errorf("Devices.Update: %w", err)
-		}
-		return d, nil
-	})
-}
-
 func (e *client) GetDevice(ctx context.Context, pid, did string, opts *packngo.ListOptions) (*packngo.Device, error) {
 	return wrap(ctx, e, func(cl *packngo.Client) (*packngo.Device, error) {
 		d, _, err := cl.Devices.Get(did, opts)