Skip to content

Commit 566d735

Browse files
authored
feat: ssh key pair and security group support for uhost (#87)
* feat: ssh key pair and security group support for uhost
1 parent 4dd4cd3 commit 566d735

File tree

1 file changed

+73
-11
lines changed

1 file changed

+73
-11
lines changed

cmd/uhost.go

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package cmd
1616

1717
import (
1818
"encoding/base64"
19+
"errors"
1920
"fmt"
2021
"io"
2122
"regexp"
@@ -39,6 +40,8 @@ import (
3940

4041
const _RetCodeRegionNoPermission = 230
4142

43+
const _MaxBoundSecGroupCount = 5
44+
4245
var uhostSpoller = base.NewSpoller(sdescribeUHostByID, base.Cxt.GetWriter())
4346

4447
// NewCmdUHost ucloud uhost
@@ -302,6 +305,10 @@ func NewCmdUHostCreate() *cobra.Command {
302305
var userData string
303306
var userDataImageFlag bool
304307
var userDataBase64 string
308+
var firewallId string
309+
var secGroupIds []string
310+
var keyPairId string
311+
var password string
305312

306313
req := base.BizClient.NewCreateUHostInstanceRequest()
307314
eipReq := uhost.CreateUHostInstanceParamNetworkInterfaceEIP{}
@@ -329,11 +336,33 @@ func NewCmdUHostCreate() *cobra.Command {
329336

330337
RunE: func(cmd *cobra.Command, args []string) error {
331338
*req.Memory *= 1024
332-
req.LoginMode = sdk.String("Password")
339+
if len(password) > 0 {
340+
req.LoginMode = sdk.String("Password")
341+
req.KeyPairId = nil
342+
req.Password = sdk.String(password)
343+
} else if len(keyPairId) > 0 {
344+
req.LoginMode = sdk.String("KeyPair")
345+
req.KeyPairId = sdk.String(keyPairId)
346+
req.Password = nil
347+
} else {
348+
return fmt.Errorf("password or key-pair-id is required")
349+
}
350+
if len(firewallId) > 0 {
351+
req.SecurityGroupId = sdk.String(firewallId)
352+
} else if len(secGroupIds) > 0 {
353+
if len(secGroupIds) > _MaxBoundSecGroupCount {
354+
return fmt.Errorf("security group count should not be more than 5")
355+
}
356+
secGroupList := make([]uhost.CreateUHostInstanceParamSecGroupId, 0)
357+
for idx, secGroupId := range secGroupIds {
358+
secGroupList = append(secGroupList, uhost.CreateUHostInstanceParamSecGroupId{Id: sdk.String(secGroupId), Priority: sdk.Int(1 + idx)})
359+
}
360+
req.SecGroupId = secGroupList
361+
req.SecurityMode = sdk.String("SecGroup")
362+
}
333363
req.ImageId = sdk.String(base.PickResourceID(*req.ImageId))
334364
req.VPCId = sdk.String(base.PickResourceID(*req.VPCId))
335365
req.SubnetId = sdk.String(base.PickResourceID(*req.SubnetId))
336-
req.SecurityGroupId = sdk.String(base.PickResourceID(*req.SecurityGroupId))
337366
req.IsolationGroup = sdk.String(base.PickResourceID(*req.IsolationGroup))
338367
if *req.Disks[1].Type == "NONE" || *req.Disks[1].Type == "" {
339368
req.Disks = req.Disks[:1]
@@ -446,7 +475,8 @@ func NewCmdUHostCreate() *cobra.Command {
446475
flags.SortFlags = false
447476
req.CPU = flags.Int("cpu", 4, "Required. The count of CPU cores. Optional parameters: {1, 2, 4, 8, 12, 16, 24, 32, 64}")
448477
req.Memory = flags.Int("memory-gb", 8, "Required. Memory size. Unit: GB. Range: [1, 512], multiple of 2")
449-
req.Password = flags.String("password", "", "Required. Password of the uhost user(root/ubuntu)")
478+
flags.StringVar(&password, "password", "", "Optional. Password of the uhost user(root/ubuntu)")
479+
flags.StringVar(&keyPairId, "key-pair-id", "", "Optional. Resource ID of ssh key pair. See 'ucloud api --Action DescribeUHostKeyPairs' Where both password and key-pair-id are set, the key-pair-id is ignored")
450480
req.ImageId = flags.String("image-id", "", "Required. The ID of image. see 'ucloud image list'")
451481
flags.BoolVar(&async, "async", false, "Optional. Do not wait for the long-running operation to finish.")
452482
flags.IntVar(&count, "count", 1, "Optional. Number of uhost to create.")
@@ -480,7 +510,8 @@ func NewCmdUHostCreate() *cobra.Command {
480510
req.Disks[1].Type = flags.String("data-disk-type", "CLOUD_SSD", "Optional. Accept values: 'LOCAL_NORMAL','LOCAL_SSD','CLOUD_NORMAL',CLOUD_SSD','CLOUD_RSSD','EXCLUSIVE_LOCAL_DISK' and 'NONE'. 'LOCAL_NORMAL', Ordinary local disk; 'CLOUD_NORMAL', Ordinary cloud disk; 'LOCAL_SSD',local ssd disk; 'CLOUD_SSD',cloud ssd disk; 'CLOUD_RSSD', coud rssd disk; 'EXCLUSIVE_LOCAL_DISK',big data. The disk only supports a limited combination. 'NONE', create uhost without data disk. More details https://docs.ucloud.cn/api/uhost-api/disk_type")
481511
req.Disks[1].Size = flags.Int("data-disk-size-gb", 20, "Optional. Disk size. Unit GB")
482512
req.Disks[1].BackupType = flags.String("data-disk-backup-type", "NONE", "Optional. Enumeration value, 'NONE' or 'DATAARK'. DataArk supports real-time backup, which can restore the disk back to any moment within the last 12 hours. (Normal Local Disk and Normal Cloud Disk Only)")
483-
req.SecurityGroupId = flags.String("firewall-id", "", "Optional. Firewall Id, default: Web recommended firewall. see 'ucloud firewall list'.")
513+
flags.StringVar(&firewallId, "firewall-id", "", "Optional. Firewall Id, default: Web recommended firewall. see 'ucloud firewall list'.")
514+
flags.StringSliceVar(&secGroupIds, "security-group-id", nil, "Optional. Security Group Id. Before using security group function, please confirm the account has such permission. When both firewall-id and security-group-id are set, the security-group-id will be ignored")
484515
req.Tag = flags.String("group", "Default", "Optional. Business group")
485516
req.IsolationGroup = flags.String("isolation-group", "", "Optional. Resource ID of isolation group. see 'ucloud uhost isolation-group list")
486517
req.GpuType = flags.String("gpu-type", "", "Optional. The type of GPU instance. Required if defined the `machine-type` as 'G'. Accept values: 'K80', 'P40', 'V100'. Forward to https://docs.ucloud.cn/api/uhost-api/uhost_type for details.")
@@ -524,7 +555,6 @@ func NewCmdUHostCreate() *cobra.Command {
524555

525556
cmd.MarkFlagRequired("cpu")
526557
cmd.MarkFlagRequired("memory-gb")
527-
cmd.MarkFlagRequired("password")
528558
cmd.MarkFlagRequired("image-id")
529559

530560
return cmd
@@ -1211,12 +1241,28 @@ func getUhostList(states []string, project, region, zone string) []string {
12111241
func NewCmdUHostClone(out io.Writer) *cobra.Command {
12121242
var uhostID *string
12131243
var async *bool
1244+
1245+
var password string
1246+
var keyPairId string
1247+
12141248
req := base.BizClient.NewCreateUHostInstanceRequest()
12151249
cmd := &cobra.Command{
12161250
Use: "clone",
12171251
Short: "Create an uhost with the same configuration as another uhost, excluding bound eip and udisk",
12181252
Long: "Create an uhost with the same configuration as another uhost, excluding bound eip and udisk",
12191253
Run: func(com *cobra.Command, args []string) {
1254+
if len(password) > 0 {
1255+
req.LoginMode = sdk.String("Password")
1256+
req.KeyPairId = nil
1257+
req.Password = sdk.String(password)
1258+
} else if len(keyPairId) > 0 {
1259+
req.LoginMode = sdk.String("KeyPair")
1260+
req.KeyPairId = sdk.String(keyPairId)
1261+
req.Password = nil
1262+
} else {
1263+
base.Cxt.PrintErr(errors.New("password or key-pair-id is required"))
1264+
return
1265+
}
12201266
*uhostID = base.PickResourceID(*uhostID)
12211267
queryReq := base.BizClient.NewDescribeUHostInstanceRequest()
12221268
queryReq.ProjectId = req.ProjectId
@@ -1232,6 +1278,10 @@ func NewCmdUHostClone(out io.Writer) *cobra.Command {
12321278
base.Cxt.PrintErr(fmt.Errorf("uhost[%s] not exist", *uhostID))
12331279
return
12341280
}
1281+
if queryResp.UHostSet[0].SecGroupInstance == true {
1282+
base.Cxt.PrintErr(fmt.Errorf("uhost[%s] is in security groups, it is not allowed to clone", *uhostID))
1283+
return
1284+
}
12351285
queryFirewallReq := base.BizClient.NewDescribeFirewallRequest()
12361286
queryFirewallReq.ProjectId = req.ProjectId
12371287
queryFirewallReq.Region = req.Region
@@ -1275,7 +1325,6 @@ func NewCmdUHostClone(out io.Writer) *cobra.Command {
12751325
req.Disks = append(req.Disks, item)
12761326
}
12771327
req.Tag = &uhostIns.Tag
1278-
req.LoginMode = sdk.String("Password")
12791328
resp, err := base.BizClient.CreateUHostInstance(req)
12801329
if err != nil {
12811330
base.HandleError(err)
@@ -1298,7 +1347,9 @@ func NewCmdUHostClone(out io.Writer) *cobra.Command {
12981347
flags := cmd.Flags()
12991348
flags.SortFlags = false
13001349
uhostID = flags.String("uhost-id", "", "Required. Resource ID of the uhost to clone from")
1301-
req.Password = flags.String("password", "", "Required. Password of the uhost user(root/ubuntu)")
1350+
flags.StringVar(&password, "password", "", "Optional. Password of the uhost user(root/ubuntu)")
1351+
flags.StringVar(&keyPairId, "key-pair-id", "", "Optional. Resource ID of ssh key pair. See 'ucloud api --Action DescribeUHostKeyPairs' Where both password and key-pair-id are set, the key-pair-id is ignored")
1352+
13021353
req.Name = flags.String("name", "", "Optional. Name of the uhost to clone")
13031354
req.ProjectId = flags.String("project-id", base.ConfigIns.ProjectID, "Optional. Assign project-id")
13041355
req.Region = flags.String("region", base.ConfigIns.Region, "Optional. Assign region")
@@ -1308,7 +1359,6 @@ func NewCmdUHostClone(out io.Writer) *cobra.Command {
13081359
return getUhostList([]string{status.HOST_RUNNING, status.HOST_STOPPED}, *req.ProjectId, *req.Region, *req.Zone)
13091360
})
13101361
cmd.MarkFlagRequired("uhost-id")
1311-
cmd.MarkFlagRequired("password")
13121362
return cmd
13131363
}
13141364

@@ -1443,6 +1493,7 @@ func checkAndCloseUhost(yes, async bool, uhostID, project, region, zone string,
14431493
// NewCmdUhostReinstallOS ucloud uhost reinstall-os
14441494
func NewCmdUhostReinstallOS(out io.Writer) *cobra.Command {
14451495
var isReserveDataDisk, yes, async *bool
1496+
var password, keyPairId string
14461497
req := base.BizClient.NewReinstallUHostInstanceRequest()
14471498
cmd := &cobra.Command{
14481499
Use: "reinstall-os",
@@ -1455,7 +1506,18 @@ func NewCmdUhostReinstallOS(out io.Writer) *cobra.Command {
14551506
req.ReserveDisk = sdk.String("No")
14561507
}
14571508
req.UHostId = sdk.String(base.PickResourceID(*req.UHostId))
1458-
req.Password = sdk.String(sdk.StringValue(req.Password))
1509+
if len(password) > 0 {
1510+
req.LoginMode = sdk.String("Password")
1511+
req.KeyPairId = nil
1512+
req.Password = sdk.String(password)
1513+
} else if len(keyPairId) > 0 {
1514+
req.LoginMode = sdk.String("KeyPair")
1515+
req.KeyPairId = sdk.String(keyPairId)
1516+
req.Password = nil
1517+
} else {
1518+
base.Cxt.PrintErr(fmt.Errorf("password or key-pair-id is required"))
1519+
return
1520+
}
14591521

14601522
any, err := describeUHostByID(*req.UHostId, *req.ProjectId, *req.Region, *req.Zone)
14611523
if err != nil {
@@ -1515,7 +1577,8 @@ func NewCmdUhostReinstallOS(out io.Writer) *cobra.Command {
15151577
flags := cmd.Flags()
15161578
flags.SortFlags = false
15171579
req.UHostId = flags.String("uhost-id", "", "Required. Resource ID of the uhost to reinstall operating system")
1518-
req.Password = flags.String("password", "", "Required. Password of the administrator")
1580+
flags.StringVar(&password, "password", "", "Optional. Password of the uhost user(root/ubuntu)")
1581+
flags.StringVar(&keyPairId, "key-pair-id", "", "Optional. Resource ID of ssh key pair. See 'ucloud api --Action DescribeUHostKeyPairs' Where both password and key-pair-id are set, the key-pair-id is ignored")
15191582
req.ImageId = flags.String("image-id", "", "Optional. Resource ID the image to install. See 'ucloud image list'. Default is original image of the uhost")
15201583
req.ProjectId = flags.String("project-id", base.ConfigIns.ProjectID, "Optional. Assign project-id")
15211584
req.Region = flags.String("region", base.ConfigIns.Region, "Optional. Assign region")
@@ -1527,7 +1590,6 @@ func NewCmdUhostReinstallOS(out io.Writer) *cobra.Command {
15271590
return getUhostList([]string{status.HOST_RUNNING, status.HOST_STOPPED}, *req.ProjectId, *req.Region, *req.Zone)
15281591
})
15291592
cmd.MarkFlagRequired("uhost-id")
1530-
cmd.MarkFlagRequired("password")
15311593
return cmd
15321594
}
15331595

0 commit comments

Comments
 (0)