Skip to content

Commit d942896

Browse files
Codelaxremyleone
andauthored
feat(instance): improve support of windows based servers (#3863)
Co-authored-by: Rémy Léone <[email protected]>
1 parent 8464a97 commit d942896

17 files changed

+5534
-42
lines changed

cmd/scw/testdata/test-all-usage-instance-server-create-usage.golden

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,25 @@ EXAMPLES:
2626
scw instance server create image=ubuntu_focal ip=$ip
2727

2828
ARGS:
29-
image=ubuntu_jammy Image ID or label of the server
30-
type=DEV1-S Server commercial type (help: https://www.scaleway.com/en/docs/compute/instances/reference-content/choosing-instance-type/)
31-
[name=<generated>] Server name
32-
[root-volume] Local root volume of the server
33-
[additional-volumes.{index}] Additional local and block volumes attached to your server
34-
[ip=new] Either an IP, an IP ID, 'new' to create a new IP, 'dynamic' to use a dynamic IP or 'none' for no public IP (new | dynamic | none | <id> | <address>)
35-
[tags.{index}] Server tags
36-
[ipv6] Enable IPv6
37-
[stopped] Do not start server after its creation
38-
[security-group-id] The security group ID used for this server
39-
[placement-group-id] The placement group ID in which the server has to be created
40-
[bootscript-id] The bootscript ID to use, if empty the local boot will be used
41-
[cloud-init] The cloud-init script to use (Support file loading with @/path/to/file)
42-
[boot-type=local] The boot type to use, if empty the local boot will be used. Will be overwritten to bootscript if bootscript-id is set. (local | bootscript | rescue)
43-
[routed-ip-enabled] Enable routed IP support
44-
[project-id] Project ID to use. If none is passed the default project ID will be used
45-
[zone=fr-par-1] Zone to target. If none is passed will use default zone from the config (fr-par-1 | fr-par-2 | fr-par-3 | nl-ams-1 | nl-ams-2 | nl-ams-3 | pl-waw-1 | pl-waw-2 | pl-waw-3)
46-
[organization-id] Organization ID to use. If none is passed the default organization ID will be used
29+
image=ubuntu_jammy Image ID or label of the server
30+
type=DEV1-S Server commercial type (help: https://www.scaleway.com/en/docs/compute/instances/reference-content/choosing-instance-type/)
31+
[name=<generated>] Server name
32+
[root-volume] Local root volume of the server
33+
[additional-volumes.{index}] Additional local and block volumes attached to your server
34+
[ip=new] Either an IP, an IP ID, 'new' to create a new IP, 'dynamic' to use a dynamic IP or 'none' for no public IP (new | dynamic | none | <id> | <address>)
35+
[tags.{index}] Server tags
36+
[ipv6] Enable IPv6
37+
[stopped] Do not start server after its creation
38+
[security-group-id] The security group ID used for this server
39+
[placement-group-id] The placement group ID in which the server has to be created
40+
[bootscript-id] The bootscript ID to use, if empty the local boot will be used
41+
[cloud-init] The cloud-init script to use (Support file loading with @/path/to/file)
42+
[boot-type=local] The boot type to use, if empty the local boot will be used. Will be overwritten to bootscript if bootscript-id is set. (local | bootscript | rescue)
43+
[routed-ip-enabled] Enable routed IP support
44+
[admin-password-encryption-ssh-key-id] ID of the IAM SSH Key used to encrypt generated admin password. Required when creating a windows server.
45+
[project-id] Project ID to use. If none is passed the default project ID will be used
46+
[zone=fr-par-1] Zone to target. If none is passed will use default zone from the config (fr-par-1 | fr-par-2 | fr-par-3 | nl-ams-1 | nl-ams-2 | nl-ams-3 | pl-waw-1 | pl-waw-2 | pl-waw-3)
47+
[organization-id] Organization ID to use. If none is passed the default organization ID will be used
4748

4849
FLAGS:
4950
-h, --help help for create

cmd/scw/testdata/test-all-usage-instance-server-get-rdp-password-usage.golden

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
22
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
3-
Get your server rdp and decrypt it using your ssh key
3+
Get your server rdp password and decrypt it using your ssh key
44

55
USAGE:
66
scw instance server get-rdp-password <server-id ...> [arg=value ...]
@@ -12,6 +12,7 @@ ARGS:
1212

1313
FLAGS:
1414
-h, --help help for get-rdp-password
15+
-w, --wait wait until the server is ready
1516

1617
GLOBAL FLAGS:
1718
-c, --config string The path to the config file

cmd/scw/testdata/test-all-usage-instance-server-usage.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ AVAILABLE COMMANDS:
1919
detach-volume Detach a volume from its server
2020
enable-routed-ip Migrate server to IP mobility
2121
get Get an Instance
22-
get-rdp-password Get your server rdp and decrypt it using your ssh key
22+
get-rdp-password Get your server rdp password and decrypt it using your ssh key
2323
list List all Instances
2424
list-actions List Instance actions
2525
reboot Reboot server

docs/commands/instance.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ This API allows you to manage your Instances.
6060
- [Detach a volume from its server](#detach-a-volume-from-its-server)
6161
- [Migrate server to IP mobility](#migrate-server-to-ip-mobility)
6262
- [Get an Instance](#get-an-instance)
63-
- [Get your server rdp and decrypt it using your ssh key](#get-your-server-rdp-and-decrypt-it-using-your-ssh-key)
63+
- [Get your server rdp password and decrypt it using your ssh key](#get-your-server-rdp-password-and-decrypt-it-using-your-ssh-key)
6464
- [List all Instances](#list-all-instances)
6565
- [List Instance actions](#list-instance-actions)
6666
- [Reboot server](#reboot-server)
@@ -1720,6 +1720,7 @@ scw instance server create [arg=value ...]
17201720
| cloud-init | | The cloud-init script to use |
17211721
| boot-type | Default: `local`<br />One of: `local`, `bootscript`, `rescue` | The boot type to use, if empty the local boot will be used. Will be overwritten to bootscript if bootscript-id is set. |
17221722
| routed-ip-enabled | | Enable routed IP support |
1723+
| admin-password-encryption-ssh-key-id | | ID of the IAM SSH Key used to encrypt generated admin password. Required when creating a windows server. |
17231724
| project-id | | Project ID to use. If none is passed the default project ID will be used |
17241725
| zone | Default: `fr-par-1`<br />One of: `fr-par-1`, `fr-par-2`, `fr-par-3`, `nl-ams-1`, `nl-ams-2`, `nl-ams-3`, `pl-waw-1`, `pl-waw-2`, `pl-waw-3` | Zone to target. If none is passed will use default zone from the config |
17251726
| organization-id | | Organization ID to use. If none is passed the default organization ID will be used |
@@ -1923,7 +1924,7 @@ scw instance server get 94ededdf-358d-4019-9886-d754f8a2e78d
19231924

19241925

19251926

1926-
### Get your server rdp and decrypt it using your ssh key
1927+
### Get your server rdp password and decrypt it using your ssh key
19271928

19281929

19291930

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ require (
2525
github.com/mattn/go-isatty v0.0.20
2626
github.com/moby/buildkit v0.13.2
2727
github.com/opencontainers/go-digest v1.0.0
28-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27.0.20240603133732-526ae94f1caa
28+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27.0.20240607082515-02c0fc2651bf
2929
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
3030
github.com/spf13/cobra v1.8.0
3131
github.com/spf13/pflag v1.0.5

go.sum

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,12 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
104104
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
105105
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
106106
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
107+
github.com/buildpacks/imgutil v0.0.0-20240514200737-4af87862ff7e/go.mod h1:n2R6VRuWsAX3cyHCp/u0Z4WJcixny0gYg075J39owrk=
107108
github.com/buildpacks/imgutil v0.0.0-20240605145725-186f89b2d168 h1:yVYVi1V7x1bXklOx9lpbTfteyzQKGZC/wkl+IlaVRlU=
108109
github.com/buildpacks/imgutil v0.0.0-20240605145725-186f89b2d168/go.mod h1:n2R6VRuWsAX3cyHCp/u0Z4WJcixny0gYg075J39owrk=
109110
github.com/buildpacks/lifecycle v0.19.6 h1:/bmfMs35aSkxyzYDF+iHl9VnYmUBBbHBmnvo8XNEINk=
110111
github.com/buildpacks/lifecycle v0.19.6/go.mod h1:sWrBJzf/7dWrcHrWiV/P2+3jS8G8Ki5tczq8jO3XVRQ=
112+
github.com/buildpacks/pack v0.34.1/go.mod h1:7y9JxLWUwtLzOOD3/DyziBN8B18r5iy56yg//5n6g5c=
111113
github.com/buildpacks/pack v0.34.2 h1:6+BvuYeS07QUJZAkb/L9v2TI7mtLA2zzbfewfB4bRT8=
112114
github.com/buildpacks/pack v0.34.2/go.mod h1:g1o7cohzXn2X+uYdWKdFgity1l2QvUDSGIMVhzBbA1M=
113115
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
@@ -184,6 +186,7 @@ github.com/docker/cli v26.1.3+incompatible h1:bUpXT/N0kDE3VUHI2r5VMsYQgi38kYuoC0
184186
github.com/docker/cli v26.1.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
185187
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
186188
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
189+
github.com/docker/docker v26.1.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
187190
github.com/docker/docker v26.1.4+incompatible h1:vuTpXDuoga+Z38m1OZHzl7NKisKWaWlhjQk7IDPSLsU=
188191
github.com/docker/docker v26.1.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
189192
github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
@@ -453,8 +456,9 @@ github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUz
453456
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
454457
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
455458
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
456-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27.0.20240603133732-526ae94f1caa h1:i+rtstvLVsx9zBDmVir/PssINIURdrXaCRpBW5+ctCs=
457459
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27.0.20240603133732-526ae94f1caa/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
460+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27.0.20240607082515-02c0fc2651bf h1:sxXcyDGxqKfCisRoooCZSmXSLSCmJv7uYsCvxkgCELo=
461+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27.0.20240607082515-02c0fc2651bf/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
458462
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
459463
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
460464
github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbmfHkLguCE9laoZCUzEEpIZXA=
@@ -561,6 +565,7 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0
561565
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
562566
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
563567
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
568+
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
564569
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
565570
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
566571
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -641,6 +646,7 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
641646
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
642647
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
643648
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
649+
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
644650
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
645651
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
646652
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -649,6 +655,7 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
649655
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
650656
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
651657
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
658+
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
652659
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
653660
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
654661
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -660,6 +667,7 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
660667
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
661668
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
662669
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
670+
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
663671
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
664672
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
665673
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=

internal/namespaces/instance/v1/custom_server.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@ func serverUpdateBuilder(c *core.Command) *core.Command {
184184

185185
c.ArgsType = reflect.TypeOf(instanceUpdateServerRequestCustom{})
186186

187+
// Add completion functions
188+
c.ArgSpecs.GetByName("admin-password-encryption-ssh-key-id").AutoCompleteFunc = completeSSHKeyID
189+
187190
// Rename modified arg specs.
188191
c.ArgSpecs.GetByName("placement-group").Name = "placement-group-id"
189192
c.ArgSpecs.GetByName("security-group.id").Name = "security-group-id"

internal/namespaces/instance/v1/custom_server_create.go

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ type instanceCreateServerRequest struct {
3333
SecurityGroupID string
3434
PlacementGroupID string
3535

36+
// Windows
37+
AdminPasswordEncryptionSSHKeyID *string
38+
3639
// IP Mobility
3740
RoutedIPEnabled *bool
3841

@@ -129,6 +132,11 @@ func serverCreateCommand() *core.Command {
129132
Name: "routed-ip-enabled",
130133
Short: "Enable routed IP support",
131134
},
135+
{
136+
Name: "admin-password-encryption-ssh-key-id",
137+
Short: "ID of the IAM SSH Key used to encrypt generated admin password. Required when creating a windows server.",
138+
AutoCompleteFunc: completeSSHKeyID,
139+
},
132140
core.ProjectIDArgSpec(),
133141
core.ZoneArgSpec((*instance.API)(nil).Zones()...),
134142
core.OrganizationIDArgSpec(),
@@ -190,20 +198,31 @@ func instanceServerCreateRun(ctx context.Context, argsI interface{}) (i interfac
190198
needIPCreation := false
191199

192200
serverReq := &instance.CreateServerRequest{
193-
Zone: args.Zone,
194-
Organization: args.OrganizationID,
195-
Project: args.ProjectID,
196-
Name: args.Name,
197-
CommercialType: args.Type,
198-
EnableIPv6: scw.BoolPtr(args.IPv6),
199-
Tags: args.Tags,
200-
RoutedIPEnabled: args.RoutedIPEnabled,
201+
Zone: args.Zone,
202+
Organization: args.OrganizationID,
203+
Project: args.ProjectID,
204+
Name: args.Name,
205+
CommercialType: args.Type,
206+
EnableIPv6: scw.BoolPtr(args.IPv6),
207+
Tags: args.Tags,
208+
RoutedIPEnabled: args.RoutedIPEnabled,
209+
AdminPasswordEncryptionSSHKeyID: args.AdminPasswordEncryptionSSHKeyID,
201210
}
202211

203212
client := core.ExtractClient(ctx)
204213
apiMarketplace := marketplace.NewAPI(client)
205214
apiInstance := instance.NewAPI(client)
206215

216+
if commercialTypeIsWindowsServer(serverReq.CommercialType) && serverReq.AdminPasswordEncryptionSSHKeyID == nil {
217+
return nil, &core.CliError{
218+
Err: core.MissingRequiredArgumentError("admin-password-encryption-ssh-key-id").Err,
219+
Details: "Expected a SSH Key ID to encrypt Admin RDP password. If not provided, no password will be generated. Key must be RSA Public Key.",
220+
Hint: "Use completion or get your ssh key id using 'scw iam ssh-key list',",
221+
Code: 1,
222+
Empty: false,
223+
}
224+
}
225+
207226
//
208227
// Image.
209228
//

internal/namespaces/instance/v1/custom_server_create_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,18 @@ func Test_CreateServerErrors(t *testing.T) {
505505
core.TestCheckExitCode(1),
506506
),
507507
}))
508+
509+
////
510+
// Windows
511+
////
512+
t.Run("Error: ssh key id is required", core.Test(&core.TestConfig{
513+
Commands: instance.GetCommands(),
514+
Cmd: "scw instance server create image=windows_server_2022 type=POP2-2C-8G-WIN",
515+
Check: core.TestCheckCombine(
516+
core.TestCheckGolden(),
517+
core.TestCheckExitCode(1),
518+
),
519+
}))
508520
}
509521

510522
func Test_CreateServerScratchStorage(t *testing.T) {

internal/namespaces/instance/v1/custom_server_rdp.go

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,16 @@ type instanceServerGetRdpPasswordRequest struct {
2626
Key string
2727
}
2828

29+
type ServerGetRdpPasswordResponse struct {
30+
Username string
31+
Password string
32+
SSHKeyID *string
33+
SSHKeyDescription string
34+
}
35+
2936
func instanceServerGetRdpPassword() *core.Command {
3037
return &core.Command{
31-
Short: `Get your server rdp and decrypt it using your ssh key`,
38+
Short: `Get your server rdp password and decrypt it using your ssh key`,
3239
Namespace: "instance",
3340
Verb: "get-rdp-password",
3441
Resource: "server",
@@ -51,6 +58,26 @@ func instanceServerGetRdpPassword() *core.Command {
5158
core.ZoneArgSpec(),
5259
},
5360
Run: instanceServerGetRdpPasswordRun,
61+
WaitFunc: func(ctx context.Context, argsI, respI interface{}) (interface{}, error) {
62+
// Wait only if response does not contain a password
63+
if _, isPasswd := respI.(*ServerGetRdpPasswordResponse); isPasswd {
64+
return respI, nil
65+
}
66+
67+
args := argsI.(*instanceServerGetRdpPasswordRequest)
68+
apiInstance := instance.NewAPI(core.ExtractClient(ctx))
69+
_, err := apiInstance.WaitForServerRDPPassword(&instance.WaitForServerRDPPasswordRequest{
70+
Zone: args.Zone,
71+
ServerID: args.ServerID,
72+
RetryInterval: core.DefaultRetryInterval,
73+
})
74+
if err != nil {
75+
return nil, err
76+
}
77+
78+
// Retry command now that encrypted password is available
79+
return instanceServerGetRdpPasswordRun(ctx, argsI)
80+
},
5481
}
5582
}
5683

@@ -85,8 +112,14 @@ func instanceServerGetRdpPasswordRun(ctx context.Context, argsI interface{}) (i
85112
if err != nil {
86113
return nil, err
87114
}
88-
if resp.Server.AdminPasswordEncryptedValue == nil {
89-
return nil, fmt.Errorf("rdp password is nil")
115+
if resp.Server.AdminPasswordEncryptedValue == nil || *resp.Server.AdminPasswordEncryptedValue == "" {
116+
return &core.CliError{
117+
Err: fmt.Errorf("rdp password is empty"),
118+
Message: "RDP password is nil or empty in api response",
119+
Details: "Your server have no RDP password available",
120+
Hint: "You may need to wait for your OS to start before having a generated RDP password, it can take more than 10 minutes.\nUse -w, --wait to wait for password to be available",
121+
Code: 1,
122+
}, nil
90123
}
91124

92125
encryptedRdpPassword, err := base64.StdEncoding.DecodeString(*resp.Server.AdminPasswordEncryptedValue)
@@ -110,12 +143,7 @@ func instanceServerGetRdpPasswordRun(ctx context.Context, argsI interface{}) (i
110143
}
111144
}
112145

113-
return struct {
114-
Username string
115-
Password string
116-
SSHKeyID *string
117-
SSHKeyDescription string
118-
}{
146+
return &ServerGetRdpPasswordResponse{
119147
Username: "Administrator",
120148
Password: string(password),
121149
SSHKeyID: resp.Server.AdminPasswordEncryptionSSHKeyID,
@@ -149,3 +177,20 @@ func parsePrivateKey(ctx context.Context, key []byte) (any, error) {
149177

150178
return privateKey, nil
151179
}
180+
181+
func completeSSHKeyID(ctx context.Context, prefix string, _ any) core.AutocompleteSuggestions {
182+
resp, err := iam.NewAPI(core.ExtractClient(ctx)).ListSSHKeys(&iam.ListSSHKeysRequest{}, scw.WithAllPages())
183+
if err != nil {
184+
return nil
185+
}
186+
187+
suggestion := make([]string, 0, len(resp.SSHKeys))
188+
189+
for _, sshKey := range resp.SSHKeys {
190+
if strings.HasPrefix(sshKey.ID, prefix) {
191+
suggestion = append(suggestion, sshKey.ID)
192+
}
193+
}
194+
195+
return suggestion
196+
}

0 commit comments

Comments
 (0)