@@ -713,6 +713,10 @@ func serverDeleteCommand() *core.Command {
713
713
},
714
714
},
715
715
SeeAlsos : []* core.SeeAlso {
716
+ {
717
+ Command : "scw instance server terminate" ,
718
+ Short : "Terminate a running server" ,
719
+ },
716
720
{
717
721
Command : "scw instance server stop" ,
718
722
Short : "Stop a running server" ,
@@ -822,3 +826,165 @@ func serverDeleteCommand() *core.Command {
822
826
},
823
827
}
824
828
}
829
+
830
+ type customTerminateServerRequest struct {
831
+ Zone scw.Zone
832
+ ServerID string
833
+ WithIP bool
834
+ WithBlock withBlock
835
+ }
836
+
837
+ type withBlock string
838
+
839
+ const (
840
+ withBlockPrompt = withBlock ("prompt" )
841
+ withBlockTrue = withBlock ("true" )
842
+ withBlockFalse = withBlock ("false" )
843
+ )
844
+
845
+ func serverTerminateCommand () * core.Command {
846
+ return & core.Command {
847
+ Short : `Terminate server` ,
848
+ Long : `Terminates a server with the given ID and all of its volumes.` ,
849
+ Namespace : "instance" ,
850
+ Verb : "terminate" ,
851
+ Resource : "server" ,
852
+ ArgsType : reflect .TypeOf (customTerminateServerRequest {}),
853
+ ArgSpecs : core.ArgSpecs {
854
+ {
855
+ Name : "server-id" ,
856
+ Required : true ,
857
+ Positional : true ,
858
+ },
859
+ {
860
+ Name : "with-ip" ,
861
+ Short : "Delete the IP attached to the server" ,
862
+ },
863
+ {
864
+ Name : "with-block" ,
865
+ Short : "Delete the Block Storage volumes attached to the server" ,
866
+ Default : core .DefaultValueSetter ("prompt" ),
867
+ EnumValues : []string {
868
+ string (withBlockPrompt ),
869
+ string (withBlockTrue ),
870
+ string (withBlockFalse ),
871
+ },
872
+ },
873
+ core .ZoneArgSpec (),
874
+ },
875
+ Examples : []* core.Example {
876
+ {
877
+ Short : "Terminate a server in the default zone with a given id" ,
878
+ Request : `{"server_id": "11111111-1111-1111-1111-111111111111"}` ,
879
+ },
880
+ {
881
+ Short : "Terminate a server in fr-par-1 zone with a given id" ,
882
+ Request : `{"zone":"fr-par-1", "server_id": "11111111-1111-1111-1111-111111111111"}` ,
883
+ },
884
+ {
885
+ Short : "Terminate a server and also delete its flexible IPs" ,
886
+ Request : `{"with_ip":true, "server_id": "11111111-1111-1111-1111-111111111111"}` ,
887
+ },
888
+ },
889
+ SeeAlsos : []* core.SeeAlso {
890
+ {
891
+ Command : "scw instance server delete" ,
892
+ Short : "delete a running server" ,
893
+ },
894
+ {
895
+ Command : "scw instance server stop" ,
896
+ Short : "Stop a running server" ,
897
+ },
898
+ },
899
+ Run : func (ctx context.Context , argsI interface {}) (interface {}, error ) {
900
+ terminateServerArgs := argsI .(* customTerminateServerRequest )
901
+
902
+ client := core .ExtractClient (ctx )
903
+ api := instance .NewAPI (client )
904
+
905
+ server , err := api .GetServer (& instance.GetServerRequest {
906
+ Zone : terminateServerArgs .Zone ,
907
+ ServerID : terminateServerArgs .ServerID ,
908
+ })
909
+ if err != nil {
910
+ return nil , err
911
+ }
912
+
913
+ deleteBlockVolumes , err := shouldDeleteBlockVolumes (server , terminateServerArgs .WithBlock )
914
+ if err != nil {
915
+ return nil , err
916
+ }
917
+
918
+ if ! deleteBlockVolumes {
919
+ // detach block storage volumes before terminating the instance to preserve them
920
+ var multiErr error
921
+ for _ , volume := range server .Server .Volumes {
922
+ if volume .VolumeType != instance .VolumeTypeBSSD {
923
+ continue
924
+ }
925
+
926
+ if _ , err := api .DetachVolume (& instance.DetachVolumeRequest {
927
+ Zone : terminateServerArgs .Zone ,
928
+ VolumeID : volume .ID ,
929
+ }); err != nil {
930
+ multiErr = multierror .Append (multiErr , err )
931
+ continue
932
+ }
933
+
934
+ _ , _ = interactive .Printf ("successfully detached volume %s\n " , volume .Name )
935
+ }
936
+
937
+ if multiErr != nil {
938
+ return nil , multiErr
939
+ }
940
+ }
941
+
942
+ if _ , err := api .ServerAction (& instance.ServerActionRequest {
943
+ Zone : terminateServerArgs .Zone ,
944
+ ServerID : terminateServerArgs .ServerID ,
945
+ Action : instance .ServerActionTerminate ,
946
+ }); err != nil {
947
+ return nil , err
948
+ }
949
+
950
+ var multiErr error
951
+ if terminateServerArgs .WithIP && server .Server .PublicIP != nil && ! server .Server .PublicIP .Dynamic {
952
+ err = api .DeleteIP (& instance.DeleteIPRequest {
953
+ Zone : terminateServerArgs .Zone ,
954
+ IP : server .Server .PublicIP .ID ,
955
+ })
956
+ if err != nil {
957
+ multiErr = multierror .Append (multiErr , err )
958
+ } else {
959
+ _ , _ = interactive .Printf ("successfully deleted ip %s\n " , server .Server .PublicIP .Address .String ())
960
+ }
961
+ }
962
+
963
+ return & core.SuccessResult {}, multiErr
964
+ },
965
+ }
966
+ }
967
+
968
+ func shouldDeleteBlockVolumes (server * instance.GetServerResponse , terminateWithBlock withBlock ) (bool , error ) {
969
+ switch terminateWithBlock {
970
+ case withBlockTrue :
971
+ return true , nil
972
+ case withBlockFalse :
973
+ return false , nil
974
+ case withBlockPrompt :
975
+ // Only prompt user if at least one block volume is attached to the instance
976
+ for _ , volume := range server .Server .Volumes {
977
+ if volume .VolumeType != instance .VolumeTypeBSSD {
978
+ continue
979
+ }
980
+
981
+ return interactive .PromptBoolWithConfig (& interactive.PromptBoolConfig {
982
+ Prompt : "Do you also want to delete block volumes attached to this instance ?" ,
983
+ DefaultValue : false ,
984
+ })
985
+ }
986
+ return false , nil
987
+ default :
988
+ return false , fmt .Errorf ("unsupported with-block value %v" , terminateWithBlock )
989
+ }
990
+ }
0 commit comments