From 27936948b729ea6dcda9d416b681f99e0261e560 Mon Sep 17 00:00:00 2001 From: lixiaojun Date: Fri, 19 Apr 2019 11:05:27 +0800 Subject: [PATCH 1/2] add preliminary test for uhost --- README.md | 3 +- base/config.go | 5 +- base/util.go | 21 ++++-- cmd/backup.go | 8 +-- cmd/bandwidth.go | 15 ++-- cmd/disk.go | 8 +-- cmd/eip.go | 8 ++- cmd/firewall.go | 21 +++--- cmd/globalssh.go | 8 ++- cmd/image.go | 8 +-- cmd/mysql.go | 6 +- cmd/project.go | 13 ++-- cmd/region.go | 7 +- cmd/root.go | 7 +- cmd/udb.go | 11 ++- cmd/uhost.go | 103 +++++++++++++------------- cmd/uhost_test.go | 180 +++++++++++++++++++++++++++++++++++++++++----- cmd/ulb.go | 18 ++--- cmd/umem.go | 12 ++-- cmd/unet.go | 2 +- cmd/uphost.go | 8 ++- cmd/vpc.go | 22 +++--- go.mod | 2 +- go.sum | 4 +- ux/document.go | 31 ++++++-- 25 files changed, 359 insertions(+), 172 deletions(-) diff --git a/README.md b/README.md index d253b865a2..6042066350 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ ## UCloud CLI - + ![](./docs/_static/ucloud_cli_demo.gif) The UCloud CLI provides a unified command line interface to UCloud services. It works on Golang SDK based on UCloud OpenAPI and supports Linux, macOS and Windows. +https://docs.ucloud.cn/software/cli/index ## Installing ucloud-cli diff --git a/base/config.go b/base/config.go index bd94d6e2f1..6bfc81b960 100644 --- a/base/config.go +++ b/base/config.go @@ -334,7 +334,7 @@ func ListAggConfig(json bool) { aggConfigs[idx].PublicKey = MosaicString(ac.PublicKey, 8, 5) } if json { - PrintJSON(aggConfigs) + PrintJSON(aggConfigs, os.Stdout) } else { PrintTableS(aggConfigs) } @@ -514,9 +514,6 @@ func init() { PrivateKey: ConfigIns.PrivateKey, } - //sdkClient 用于上报数据 - // SdkClient = sdk.NewClient(ClientConfig, AuthCredential) - //bizClient 用于调用业务接口 BizClient = NewClient(ClientConfig, AuthCredential) } diff --git a/base/util.go b/base/util.go index bb29cc5cd3..8ff971f8a3 100644 --- a/base/util.go +++ b/base/util.go @@ -46,13 +46,18 @@ var Logger = log.New() var mu sync.Mutex func init() { - file, err := os.Create(GetHomePath() + fmt.Sprintf("/%s/cli.log", ConfigPath)) + file, err := os.Create(GetLogFilePath()) if err != nil { return } Logger.SetOutput(file) } +//GetLogFilePath 获取日志文件路径 +func GetLogFilePath() string { + return GetHomePath() + fmt.Sprintf("/%s/cli.log", ConfigPath) +} + //Log 记录日志 func Log(logs []string) { mu.Lock() @@ -177,12 +182,12 @@ func ParseError(err error) string { } //PrintJSON 以JSON格式打印数据集合 -func PrintJSON(dataSet interface{}) error { +func PrintJSON(dataSet interface{}, out io.Writer) error { bytes, err := json.MarshalIndent(dataSet, "", " ") if err != nil { return err } - Cxt.Println(string(bytes)) + fmt.Fprintln(out, string(bytes)) return nil } @@ -204,9 +209,9 @@ func PrintTableS(dataSet interface{}) { } //PrintList 打印表格或者JSON -func PrintList(dataSet interface{}) { +func PrintList(dataSet interface{}, out io.Writer) { if Global.JSON { - PrintJSON(dataSet) + PrintJSON(dataSet, out) } else { PrintTableS(dataSet) } @@ -215,7 +220,7 @@ func PrintList(dataSet interface{}) { //PrintDescribe 打印详情 func PrintDescribe(attrs []DescribeTableRow, json bool) { if json { - PrintJSON(attrs) + PrintJSON(attrs, os.Stdout) } else { for _, attr := range attrs { fmt.Println(attr.Attribute) @@ -275,7 +280,9 @@ func printTable(rowList []map[string]interface{}, fieldList []string, fieldWidth tmpl := "%-" + strconv.Itoa(fieldWidthMap[field]+GAP) + "s" fmt.Printf(tmpl, field) } - fmt.Printf("\n") + if len(fieldList) != 0 { + fmt.Printf("\n") + } //打印数据 for _, row := range rowList { diff --git a/cmd/backup.go b/cmd/backup.go index 898a9f5095..b31a400bd0 100644 --- a/cmd/backup.go +++ b/cmd/backup.go @@ -36,7 +36,7 @@ func NewCmdUDBBackup() *cobra.Command { } out := base.Cxt.GetWriter() cmd.AddCommand(NewCmdUDBBackupCreate(out)) - cmd.AddCommand(NewCmdUDBBackupList()) + cmd.AddCommand(NewCmdUDBBackupList(out)) cmd.AddCommand(NewCmdUDBBackupDelete(out)) cmd.AddCommand(NewCmdUDBBackupGetDownloadURL(out)) return cmd @@ -92,7 +92,7 @@ type udbBackupRow struct { } //NewCmdUDBBackupList ucloud udb backup list -func NewCmdUDBBackupList() *cobra.Command { +func NewCmdUDBBackupList(out io.Writer) *cobra.Command { var bpType, dbType, beginTime, endTime, backupID string bpTypeMap := map[string]int{ "manual": 1, @@ -161,7 +161,7 @@ func NewCmdUDBBackupList() *cobra.Command { } list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } flags := cmd.Flags() @@ -431,7 +431,7 @@ func NewCmdUDBLogArchiveList(out io.Writer) *cobra.Command { } list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } diff --git a/cmd/bandwidth.go b/cmd/bandwidth.go index d24304a6ce..5538b1ab89 100644 --- a/cmd/bandwidth.go +++ b/cmd/bandwidth.go @@ -16,6 +16,7 @@ package cmd import ( "fmt" + "io" "strconv" "strings" "time" @@ -47,8 +48,9 @@ func NewCmdSharedBW() *cobra.Command { Short: "Create and manipulate shared bandwidth instances", Long: "Create and manipulate shared bandwidth instances", } + out := base.Cxt.GetWriter() cmd.AddCommand(NewCmdSharedBWCreate()) - cmd.AddCommand(NewCmdSharedBWList()) + cmd.AddCommand(NewCmdSharedBWList(out)) cmd.AddCommand(NewCmdSharedBWResize()) cmd.AddCommand(NewCmdSharedBWDelete()) return cmd @@ -100,7 +102,7 @@ type SharedBWRow struct { } //NewCmdSharedBWList ucloud shared-bw list -func NewCmdSharedBWList() *cobra.Command { +func NewCmdSharedBWList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeShareBandwidthRequest() cmd := &cobra.Command{ Use: "list", @@ -132,7 +134,7 @@ func NewCmdSharedBWList() *cobra.Command { row.EIP = strings.Join(eipList, "\n") list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } flags := cmd.Flags() @@ -247,8 +249,9 @@ func NewCmdBandwidthPkg() *cobra.Command { Short: "List, create and delete bandwidth package instances", Long: "List, create and delete bandwidth package instances", } + out := base.Cxt.GetWriter() cmd.AddCommand(NewCmdBandwidthPkgCreate()) - cmd.AddCommand(NewCmdBandwidthPkgList()) + cmd.AddCommand(NewCmdBandwidthPkgList(out)) cmd.AddCommand(NewCmdBandwidthPkgDelete()) return cmd } @@ -330,7 +333,7 @@ type BandwidthPkgRow struct { } //NewCmdBandwidthPkgList ucloud bw-pkg list -func NewCmdBandwidthPkgList() *cobra.Command { +func NewCmdBandwidthPkgList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeBandwidthPackageRequest() cmd := &cobra.Command{ Use: "list", @@ -357,7 +360,7 @@ func NewCmdBandwidthPkgList() *cobra.Command { row.EIP = eip list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } flags := cmd.Flags() diff --git a/cmd/disk.go b/cmd/disk.go index 4178f13cb8..6d15675f75 100644 --- a/cmd/disk.go +++ b/cmd/disk.go @@ -39,7 +39,7 @@ func NewCmdDisk() *cobra.Command { } writer := base.Cxt.GetWriter() cmd.AddCommand(NewCmdDiskCreate(writer)) - cmd.AddCommand(NewCmdDiskList()) + cmd.AddCommand(NewCmdDiskList(writer)) cmd.AddCommand(NewCmdDiskAttach(writer)) cmd.AddCommand(NewCmdDiskDetach(writer)) cmd.AddCommand(NewCmdDiskDelete()) @@ -176,7 +176,7 @@ type DiskRow struct { } //NewCmdDiskList ucloud disk list -func NewCmdDiskList() *cobra.Command { +func NewCmdDiskList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeUDiskRequest() typeMap := map[string]string{ "DataDisk": "Oridinary-Data-Disk", @@ -222,7 +222,7 @@ func NewCmdDiskList() *cobra.Command { } list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } flags := cmd.Flags() @@ -670,7 +670,7 @@ func NewCmdSnapshotList(out io.Writer) *cobra.Command { } list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } diff --git a/cmd/eip.go b/cmd/eip.go index 533b0edc6a..1db4c0df7e 100644 --- a/cmd/eip.go +++ b/cmd/eip.go @@ -16,6 +16,7 @@ package cmd import ( "fmt" + "io" "net" "strconv" "strings" @@ -38,7 +39,8 @@ func NewCmdEIP() *cobra.Command { Long: `Manipulate EIP, such as list,allocate and release`, Args: cobra.NoArgs, } - cmd.AddCommand(NewCmdEIPList()) + out := base.Cxt.GetWriter() + cmd.AddCommand(NewCmdEIPList(out)) cmd.AddCommand(NewCmdEIPAllocate()) cmd.AddCommand(NewCmdEIPRelease()) cmd.AddCommand(NewCmdEIPBind()) @@ -64,7 +66,7 @@ type EIPRow struct { } //NewCmdEIPList ucloud eip list -func NewCmdEIPList() *cobra.Command { +func NewCmdEIPList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeEIPRequest() fetchAll := sdk.Bool(false) cmd := &cobra.Command{ @@ -108,7 +110,7 @@ func NewCmdEIPList() *cobra.Command { row.ExpirationTime = time.Unix(int64(eip.ExpireTime), 0).Format("2006-01-02") list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } diff --git a/cmd/firewall.go b/cmd/firewall.go index 1b7aade303..ff13d852a0 100644 --- a/cmd/firewall.go +++ b/cmd/firewall.go @@ -19,7 +19,6 @@ import ( "fmt" "io" "os" - "strconv" "strings" "github.com/spf13/cobra" @@ -39,14 +38,14 @@ func NewCmdFirewall() *cobra.Command { Args: cobra.NoArgs, } writer := base.Cxt.GetWriter() - cmd.AddCommand(NewCmdFirewallList()) + cmd.AddCommand(NewCmdFirewallList(writer)) cmd.AddCommand(NewCmdFirewallCreate(writer)) cmd.AddCommand(NewCmdFirewallAddRule(writer)) cmd.AddCommand(NewCmdFirewallDeleteRule(writer)) cmd.AddCommand(NewCmdFirewallApply()) cmd.AddCommand(NewCmdFirewallCopy()) cmd.AddCommand(NewCmdFirewallDelete()) - cmd.AddCommand(NewCmdFirewallResource()) + cmd.AddCommand(NewCmdFirewallResource(writer)) cmd.AddCommand(NewCmdFirewallUpdate(writer)) return cmd @@ -64,7 +63,7 @@ type FirewallRow struct { } //NewCmdFirewallList ucloud firewall list -func NewCmdFirewallList() *cobra.Command { +func NewCmdFirewallList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeFirewallRequest() cmd := &cobra.Command{ Use: "list", @@ -94,7 +93,7 @@ func NewCmdFirewallList() *cobra.Command { } list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } flags := cmd.Flags() @@ -104,8 +103,8 @@ func NewCmdFirewallList() *cobra.Command { req.FWId = flags.String("firewall-id", "", "Optional. The Rsource ID of firewall. Return all firewalls by default.") req.ResourceType = flags.String("bound-resource-type", "", "Optional. The type of resource bound on the firewall") req.ResourceId = flags.String("bound-resource-id", "", "Optional. The resource ID of resource bound on the firewall") - req.Offset = flags.String("offset", "0", "Optional. Offset") - req.Limit = flags.String("limit", "50", "Optional. Limit") + req.Offset = flags.Int("offset", 0, "Optional. Offset") + req.Limit = flags.Int("limit", 50, "Optional. Limit") return cmd } @@ -464,7 +463,7 @@ type FirewallResourceRow struct { } //NewCmdFirewallResource ucloud firewall resource -func NewCmdFirewallResource() *cobra.Command { +func NewCmdFirewallResource(out io.Writer) *cobra.Command { fwID := "" req := base.BizClient.NewDescribeFirewallResourceRequest() cmd := &cobra.Command{ @@ -489,7 +488,7 @@ func NewCmdFirewallResource() *cobra.Command { row.Remark = rs.Remark list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } flags := cmd.Flags() @@ -597,8 +596,8 @@ func getAllFirewallIns(project, region string) ([]unet.FirewallDataSet, error) { req.Region = sdk.String(region) list := []unet.FirewallDataSet{} for offset, limit := 0, 100; ; offset += limit { - req.Offset = sdk.String(strconv.Itoa(offset)) - req.Limit = sdk.String(strconv.Itoa(limit)) + req.Offset = sdk.Int(offset) + req.Limit = sdk.Int(limit) resp, err := base.BizClient.DescribeFirewall(req) if err != nil { return nil, err diff --git a/cmd/globalssh.go b/cmd/globalssh.go index b5c04bc027..bbacc5a560 100644 --- a/cmd/globalssh.go +++ b/cmd/globalssh.go @@ -16,6 +16,7 @@ package cmd import ( "fmt" + "io" "net" "strings" @@ -34,7 +35,8 @@ func NewCmdGssh() *cobra.Command { Short: "Create,list,update and delete globalssh instance", Long: `Create,list,update and delete globalssh instance`, } - cmd.AddCommand(NewCmdGsshList()) + out := base.Cxt.GetWriter() + cmd.AddCommand(NewCmdGsshList(out)) cmd.AddCommand(NewCmdGsshCreate()) cmd.AddCommand(NewCmdGsshDelete()) cmd.AddCommand(NewCmdGsshModify()) @@ -53,7 +55,7 @@ type GSSHRow struct { } //NewCmdGsshList ucloud gssh list -func NewCmdGsshList() *cobra.Command { +func NewCmdGsshList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeGlobalSSHInstanceRequest() cmd := &cobra.Command{ Use: "list", @@ -90,7 +92,7 @@ func NewCmdGsshList() *cobra.Command { } list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) } }, } diff --git a/cmd/image.go b/cmd/image.go index d48d3fb03e..43319247ed 100644 --- a/cmd/image.go +++ b/cmd/image.go @@ -37,7 +37,7 @@ func NewCmdUImage() *cobra.Command { Args: cobra.NoArgs, } writer := base.Cxt.GetWriter() - cmd.AddCommand(NewCmdUImageList()) + cmd.AddCommand(NewCmdUImageList(writer)) cmd.AddCommand(NewCmdImageCopy(writer)) cmd.AddCommand(NewCmdUImageDelete()) createImageCmd := NewCmdUhostCreateImage(writer) @@ -58,7 +58,7 @@ type ImageRow struct { } //NewCmdUImageList ucloud uimage list -func NewCmdUImageList() *cobra.Command { +func NewCmdUImageList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeImageRequest() cmd := &cobra.Command{ Use: "list", @@ -84,13 +84,13 @@ func NewCmdUImageList() *cobra.Command { list = append(list, row) } } - base.PrintList(list) + base.PrintList(list, out) }, } req.ProjectId = cmd.Flags().String("project-id", base.ConfigIns.ProjectID, "Optional. Assign project-id") req.Region = cmd.Flags().String("region", base.ConfigIns.Region, "Optional. Assign region") req.Zone = cmd.Flags().String("zone", "", "Optional. Assign availability zone") - req.ImageType = cmd.Flags().String("image-type", "", "Optional. 'Base',Standard image; 'Business',image market; 'Custom',custom image; Return all types by default") + req.ImageType = cmd.Flags().String("image-type", "Base", "Optional. 'Base',Standard image; 'Business',image market; 'Custom',custom image") req.OsType = cmd.Flags().String("os-type", "", "Optional. Linux or Windows. Return all types by default") req.ImageId = cmd.Flags().String("image-id", "", "Optional. Resource ID of image") req.Offset = cmd.Flags().Int("offset", 0, "Optional. Offset default 0") diff --git a/cmd/mysql.go b/cmd/mysql.go index 46745edb9b..4f52ceede2 100644 --- a/cmd/mysql.go +++ b/cmd/mysql.go @@ -58,7 +58,7 @@ func NewCmdMysqlDB(out io.Writer) *cobra.Command { Long: "Manange MySQL instances", } - cmd.AddCommand(NewCmdUDBList()) + cmd.AddCommand(NewCmdUDBList(out)) cmd.AddCommand(NewCmdMysqlCreate(out)) cmd.AddCommand(NewCmdUDBDelete(out)) cmd.AddCommand(NewCmdUDBStart(out)) @@ -196,7 +196,7 @@ type UDBMysqlRow struct { } //NewCmdUDBList ucloud udb list -func NewCmdUDBList() *cobra.Command { +func NewCmdUDBList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeUDBInstanceRequest() cmd := &cobra.Command{ Use: "list", @@ -244,7 +244,7 @@ func NewCmdUDBList() *cobra.Command { list = append(list, row) } } - base.PrintList(list) + base.PrintList(list, out) }, } flags := cmd.Flags() diff --git a/cmd/project.go b/cmd/project.go index 3eea71dbbf..aa124b18a8 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -15,6 +15,8 @@ package cmd import ( + "io" + "github.com/spf13/cobra" "github.com/ucloud/ucloud-sdk-go/services/uaccount" @@ -30,7 +32,8 @@ func NewCmdProject() *cobra.Command { Long: "List,create,update and delete project", Example: "ucloud project", } - cmd.AddCommand(NewCmdProjectList()) + out := base.Cxt.GetWriter() + cmd.AddCommand(NewCmdProjectList(out)) cmd.AddCommand(NewCmdProjectCreate()) cmd.AddCommand(NewCmdProjectUpdate()) cmd.AddCommand(NewCmdProjectDelete()) @@ -38,14 +41,14 @@ func NewCmdProject() *cobra.Command { } //NewCmdProjectList ucloud project list -func NewCmdProjectList() *cobra.Command { +func NewCmdProjectList(out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "List project", Long: "List project", Example: "ucloud project list", Run: func(cmd *cobra.Command, args []string) { - listProject() + listProject(out) }, } return cmd @@ -132,7 +135,7 @@ func NewCmdProjectDelete() *cobra.Command { return cmd } -func listProject() error { +func listProject(out io.Writer) error { req := &uaccount.GetProjectListRequest{} resp, err := base.BizClient.GetProjectList(req) if err != nil { @@ -142,7 +145,7 @@ func listProject() error { return base.HandleBizError(resp) } if global.JSON { - base.PrintJSON(resp.ProjectSet) + base.PrintJSON(resp.ProjectSet, out) } else { base.PrintTable(resp.ProjectSet, []string{"ProjectId", "ProjectName"}) } diff --git a/cmd/region.go b/cmd/region.go index e27dbb4cd4..155b9f70fe 100644 --- a/cmd/region.go +++ b/cmd/region.go @@ -17,6 +17,7 @@ package cmd import ( "encoding/json" "fmt" + "io" "io/ioutil" "strings" @@ -28,8 +29,8 @@ import ( ) //NewCmdRegion ucloud region -func NewCmdRegion() *cobra.Command { - var cmd = &cobra.Command{ +func NewCmdRegion(out io.Writer) *cobra.Command { + cmd := &cobra.Command{ Use: "region", Short: "List all region and zone", Long: "List all region and zone", @@ -44,7 +45,7 @@ func NewCmdRegion() *cobra.Command { for region, zones := range regionMap { regionList = append(regionList, RegionTable{region, strings.Join(zones, ", ")}) } - base.PrintList(regionList) + base.PrintList(regionList, out) }, } return cmd diff --git a/cmd/root.go b/cmd/root.go index a2deba6371..82e0fb6585 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -27,7 +27,7 @@ import ( "github.com/ucloud/ucloud-cli/base" ) -var global = base.Global +var global = &base.Global //NewCmdRoot 创建rootCmd rootCmd represents the base command when called without any subcommands func NewCmdRoot() *cobra.Command { @@ -62,7 +62,7 @@ func NewCmdRoot() *cobra.Command { cmd.AddCommand(NewCmdInit()) cmd.AddCommand(NewCmdDoc(out)) cmd.AddCommand(NewCmdConfig()) - cmd.AddCommand(NewCmdRegion()) + cmd.AddCommand(NewCmdRegion(out)) cmd.AddCommand(NewCmdProject()) cmd.AddCommand(NewCmdUHost()) cmd.AddCommand(NewCmdUPHost()) @@ -215,7 +215,8 @@ func initialize(cmd *cobra.Command) { base.ClientConfig.Zone = zone } - if global.Debug { + mode := os.Getenv("UCLOUD_CLI_DEBUG") + if mode == "on" || global.Debug { base.ClientConfig.LogLevel = log.DebugLevel base.BizClient = base.NewClient(base.ClientConfig, base.AuthCredential) } diff --git a/cmd/udb.go b/cmd/udb.go index 9e91adafd4..9dc0adf1f9 100644 --- a/cmd/udb.go +++ b/cmd/udb.go @@ -40,7 +40,7 @@ func NewCmdUDBConf() *cobra.Command { Long: "List and manipulate configuration files of MySQL instances", } out := base.Cxt.GetWriter() - cmd.AddCommand(NewCmdUDBConfList()) + cmd.AddCommand(NewCmdUDBConfList(out)) cmd.AddCommand(NewCmdUDBConfDescribe(out)) cmd.AddCommand(NewCmdUDBConfClone(out)) cmd.AddCommand(NewCmdUDBConfUpload(out)) @@ -71,7 +71,7 @@ var dbTypeMap = map[string]string{ var dbTypeList = []string{"mysql", "mongodb", "postgresql", "sqlserver"} //NewCmdUDBConfList ucloud mysql conf list -func NewCmdUDBConfList() *cobra.Command { +func NewCmdUDBConfList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeUDBParamGroupRequest() cmd := &cobra.Command{ Use: "list", @@ -98,7 +98,7 @@ func NewCmdUDBConfList() *cobra.Command { } list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } @@ -161,7 +161,7 @@ func NewCmdUDBConfDescribe(out io.Writer) *cobra.Command { base.DescribeTableRow{Attribute: "Zone", Content: conf.Zone}, } fmt.Fprintln(out, "Attributes:") - base.PrintList(attrs) + base.PrintList(attrs, out) params := []UDBConfParamRow{} for _, p := range conf.ParamMember { @@ -175,8 +175,7 @@ func NewCmdUDBConfDescribe(out io.Writer) *cobra.Command { params = append(params, row) } fmt.Fprintln(out, "\nParameters:") - base.PrintList(params) - + base.PrintList(params, out) }, } flags := cmd.Flags() diff --git a/cmd/uhost.go b/cmd/uhost.go index e8e2b49629..05584aff9c 100644 --- a/cmd/uhost.go +++ b/cmd/uhost.go @@ -44,13 +44,13 @@ func NewCmdUHost() *cobra.Command { Args: cobra.NoArgs, } out := base.Cxt.GetWriter() - cmd.AddCommand(NewCmdUHostList()) - cmd.AddCommand(NewCmdUHostCreate(out)) + cmd.AddCommand(NewCmdUHostList(out)) + cmd.AddCommand(NewCmdUHostCreate()) cmd.AddCommand(NewCmdUHostDelete(out)) cmd.AddCommand(NewCmdUHostStop(out)) cmd.AddCommand(NewCmdUHostStart(out)) cmd.AddCommand(NewCmdUHostReboot(out)) - cmd.AddCommand(NewCmdUHostPoweroff()) + cmd.AddCommand(NewCmdUHostPoweroff(out)) cmd.AddCommand(NewCmdUHostResize(out)) cmd.AddCommand(NewCmdUHostClone(out)) cmd.AddCommand(NewCmdUhostResetPassword(out)) @@ -75,7 +75,7 @@ type UHostRow struct { } //NewCmdUHostList [ucloud uhost list] -func NewCmdUHostList() *cobra.Command { +func NewCmdUHostList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeUHostInstanceRequest() cmd := &cobra.Command{ Use: "list", @@ -118,7 +118,7 @@ func NewCmdUHostList() *cobra.Command { row.Type = host.UHostType + "/" + host.HostType list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } cmd.Flags().SortFlags = false @@ -134,9 +134,8 @@ func NewCmdUHostList() *cobra.Command { } //NewCmdUHostCreate [ucloud uhost create] -func NewCmdUHostCreate(out io.Writer) *cobra.Command { +func NewCmdUHostCreate() *cobra.Command { var bindEipIDs []string - var password string var async bool var count int @@ -153,17 +152,19 @@ func NewCmdUHostCreate(out io.Writer) *cobra.Command { req.VPCId = sdk.String(base.PickResourceID(*req.VPCId)) req.SubnetId = sdk.String(base.PickResourceID(*req.SubnetId)) req.SecurityGroupId = sdk.String(base.PickResourceID(*req.SecurityGroupId)) + + wg := &sync.WaitGroup{} + tokens := make(chan struct{}, 10) + wg.Add(count) if count <= 5 { for i := 0; i < count; i++ { bindEipID := "" if len(bindEipIDs) > i { bindEipID = bindEipIDs[i] } - go createUhost(req, eipReq, bindEipID, password, async, out, make(chan bool, count), nil, nil) + go createUhostWrapper(req, eipReq, bindEipID, async, make(chan bool, count), wg, tokens) } - <-ux.Doc.Done } else { - wg := &sync.WaitGroup{} retCh := make(chan bool, count) ux.Doc.Disable() @@ -174,16 +175,14 @@ func NewCmdUHostCreate(out io.Writer) *cobra.Command { } refresh := ux.NewRefresh() - wg.Add(count) go func() { - tokens := make(chan struct{}, 10) for i := 0; i < count; i++ { bindEipID := "" if len(bindEipIDs) > i { bindEipID = bindEipIDs[i] } - go createUhost(req, eipReq, bindEipID, password, async, out, retCh, wg, tokens) + go createUhostWrapper(req, eipReq, bindEipID, async, retCh, wg, tokens) } }() @@ -196,10 +195,13 @@ func NewCmdUHostCreate(out io.Writer) *cobra.Command { result["fail"]++ } refresh.Do(fmt.Sprintf("uhost creating, total:%d, success:%d, fail:%d", result["total"], result["success"], result["fail"])) + if result["total"] == result["success"]+result["fail"] && result["fail"] > 0 { + fmt.Printf("Check logs in %s\n", base.GetLogFilePath()) + } } }() - wg.Wait() } + wg.Wait() }, } @@ -222,7 +224,7 @@ func NewCmdUHostCreate(out io.Writer) *cobra.Command { flags.SortFlags = false req.CPU = flags.Int("cpu", 4, "Required. The count of CPU cores. Optional parameters: {1, 2, 4, 8, 12, 16, 24, 32}") req.Memory = flags.Int("memory-gb", 8, "Required. Memory size. Unit: GB. Range: [1, 128], multiple of 2") - flags.StringVar(&password, "password", "", "Required. Password of the uhost user(root/ubuntu)") + req.Password = flags.String("password", "", "Required. Password of the uhost user(root/ubuntu)") req.ImageId = flags.String("image-id", "", "Required. The ID of image. see 'ucloud image list'") flags.BoolVar(&async, "async", false, "Optional. Do not wait for the long-running operation to finish.") flags.IntVar(&count, "count", 1, "Optional. Number of uhost to create.") @@ -289,48 +291,47 @@ func NewCmdUHostCreate(out io.Writer) *cobra.Command { return cmd } -func createUhost(req *uhost.CreateUHostInstanceRequest, eipReq *unet.AllocateEIPRequest, bindEipID, password string, async bool, out io.Writer, retCh chan<- bool, wg *sync.WaitGroup, tokens chan struct{}) { +//createUhostWrapper 处理UI和并发控制 +func createUhostWrapper(req *uhost.CreateUHostInstanceRequest, eipReq *unet.AllocateEIPRequest, bindEipID string, async bool, retCh chan<- bool, wg *sync.WaitGroup, tokens chan struct{}) { //控制并发数量 - if tokens != nil { - tokens <- struct{}{} - defer func() { - <-tokens - }() - } - if wg != nil { - defer wg.Done() - } - req.Password = sdk.String(password) + tokens <- struct{}{} + defer func() { + <-tokens + wg.Done() + }() + + success, logs := createUhost(req, eipReq, bindEipID, async) + base.Log(logs) + retCh <- success +} + +func createUhost(req *uhost.CreateUHostInstanceRequest, eipReq *unet.AllocateEIPRequest, bindEipID string, async bool) (bool, []string) { resp, err := base.BizClient.CreateUHostInstance(req) block := ux.NewBlock() ux.Doc.Append(block) logs := []string{} - defer func() { - base.Log(logs) - }() logs = append(logs, fmt.Sprintf("request:%v", base.ToQueryMap(req))) if err != nil { logs = append(logs, fmt.Sprintf("err:%v", err)) block.Append(base.ParseError(err)) block.AppendDone() - retCh <- false - return + return false, logs } + logs = append(logs, fmt.Sprintf("resp:%#v", resp)) - if len(resp.UHostIds) == 1 { - text := fmt.Sprintf("uhost[%s] is initializing", resp.UHostIds[0]) - if async { - block.Append(text) - } else { - uhostSpoller.Sspoll(resp.UHostIds[0], text, []string{status.HOST_RUNNING, status.HOST_FAIL}, block) - } - retCh <- true - } else { + if len(resp.UHostIds) != 1 { block.Append(fmt.Sprintf("expect uhost count 1 , accept %d", len(resp.UHostIds))) block.AppendDone() - retCh <- false - return + return false, logs } + + text := fmt.Sprintf("uhost[%s] is initializing", resp.UHostIds[0]) + if async { + block.Append(text) + } else { + uhostSpoller.Sspoll(resp.UHostIds[0], text, []string{status.HOST_RUNNING, status.HOST_FAIL}, block) + } + if bindEipID != "" { eip := base.PickResourceID(bindEipID) logs = append(logs, fmt.Sprintf("bind eip: %s", eip)) @@ -372,6 +373,7 @@ func createUhost(req *uhost.CreateUHostInstanceRequest, eipReq *unet.AllocateEIP } } block.AppendDone() + return true, logs } //NewCmdUHostDelete ucloud uhost delete @@ -422,7 +424,7 @@ func NewCmdUHostDelete(out io.Writer) *cobra.Command { if err != nil { base.HandleError(err) } else { - base.Cxt.Printf("uhost[%s] deleted\n", resp.UHostId) + fmt.Fprintf(out, "uhost[%s] deleted\n", resp.UHostId) } } }, @@ -514,7 +516,7 @@ func NewCmdUHostStart(out io.Writer) *cobra.Command { if err != nil { base.HandleError(err) } else { - text := fmt.Sprintf("uhost:[%v] is starting", resp.UhostId) + text := fmt.Sprintf("uhost[%v] is starting", resp.UhostId) if *async { fmt.Fprintln(out, text) } else { @@ -556,7 +558,7 @@ func NewCmdUHostReboot(out io.Writer) *cobra.Command { if err != nil { base.HandleError(err) } else { - text := fmt.Sprintf("UHost:[%v] is restarting", resp.UhostId) + text := fmt.Sprintf("uhost[%v] is restarting", resp.UhostId) if *async { fmt.Fprintln(out, text) } else { @@ -582,7 +584,7 @@ func NewCmdUHostReboot(out io.Writer) *cobra.Command { } //NewCmdUHostPoweroff ucloud uhost poweroff -func NewCmdUHostPoweroff() *cobra.Command { +func NewCmdUHostPoweroff(out io.Writer) *cobra.Command { var yes *bool var uhostIDs *[]string req := base.BizClient.NewPoweroffUHostInstanceRequest() @@ -599,7 +601,7 @@ func NewCmdUHostPoweroff() *cobra.Command { } sure, err := ux.Prompt(confirmText) if err != nil { - base.Cxt.Println(err) + fmt.Fprintln(out, err) return } if !sure { @@ -613,7 +615,7 @@ func NewCmdUHostPoweroff() *cobra.Command { if err != nil { base.HandleError(err) } else { - base.Cxt.Printf("UHost:[%v] is power off\n", resp.UhostId) + fmt.Fprintf(out, "uhost[%v] is power off\n", resp.UhostId) } } }, @@ -624,7 +626,12 @@ func NewCmdUHostPoweroff() *cobra.Command { req.Region = cmd.Flags().String("region", base.ConfigIns.Region, "Assign region") req.Zone = cmd.Flags().String("zone", "", "Assign availability zone") yes = cmd.Flags().BoolP("yes", "y", false, "Optional. Do not prompt for confirmation.") + + cmd.Flags().SetFlagValuesFunc("uhost-id", func() []string { + return getUhostList([]string{status.HOST_FAIL, status.HOST_RUNNING, status.HOST_STOPPED}, *req.ProjectId, *req.Region, *req.Zone) + }) cmd.MarkFlagRequired("uhost-id") + return cmd } diff --git a/cmd/uhost_test.go b/cmd/uhost_test.go index 2f7c2469c0..f95302c5de 100644 --- a/cmd/uhost_test.go +++ b/cmd/uhost_test.go @@ -2,10 +2,14 @@ package cmd import ( "bytes" + "encoding/json" "fmt" "regexp" "strings" "testing" + "time" + + "github.com/ucloud/ucloud-cli/ux" ) type listUhostTest struct { @@ -14,43 +18,70 @@ type listUhostTest struct { } func (test listUhostTest) run(t *testing.T) { - cmd := NewCmdUHostList() + buf := new(bytes.Buffer) + cmd := NewCmdUHostList(buf) if err := cmd.Execute(); err != nil { t.Fatalf("unexpected error executing command:%v", err) } } +type listImageTest struct { + flags []string +} + +func (test *listImageTest) run(t *testing.T) string { + global.JSON = true + buf := new(bytes.Buffer) + cmd := NewCmdUImageList(buf) + cmd.Flags().Parse(test.flags) + if err := cmd.Execute(); err != nil { + t.Fatalf("unexpected error executing command: %v, flags: %v", err, test.flags) + } + + var images []ImageRow + err := json.Unmarshal(buf.Bytes(), &images) + if err != nil { + t.Fatalf("unexpected error of fetching image list: %v", err) + } + if len(images) == 0 { + t.Fatalf("image list is empty") + } + + return images[0].ImageID +} + type createUHostTest struct { flags []string - uhostIds []string + uhostIDs []string expectedOutRegexp *regexp.Regexp } -func (test createUHostTest) run(t *testing.T) { - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdUHostCreate(buf) +func (test *createUHostTest) run(t *testing.T) { + cmd := NewCmdUHostCreate() cmd.Flags().Parse(test.flags) if err := cmd.Execute(); err != nil { t.Fatalf("unexpected error executing command: %v, flags: %v", err, test.flags) } - list := test.expectedOutRegexp.FindStringSubmatch(buf.String()) + lines := ux.Doc.Content() + content := strings.Join(lines, "\n") + list := test.expectedOutRegexp.FindStringSubmatch(content) if list == nil { - t.Errorf("unexpect output:%s", buf.String()) + t.Errorf("unexpect output:%s", content) } else { if len(list) == 2 { - test.uhostIds = append(test.uhostIds, list[1]) + test.uhostIDs = append(test.uhostIDs, list[1]) } } } type deleteUHostTest struct { flags []string - uhostIds []string + uhostIDs []string expectedOutRegexp *regexp.Regexp } -func (test deleteUHostTest) run(t *testing.T) { - buf := bytes.NewBuffer([]byte{}) +func (test *deleteUHostTest) run(t *testing.T) { + buf := new(bytes.Buffer) cmd := NewCmdUHostDelete(buf) cmd.Flags().Parse(test.flags) if err := cmd.Execute(); err != nil { @@ -62,23 +93,134 @@ func (test deleteUHostTest) run(t *testing.T) { } } -func TestCreateUhost(t *testing.T) { - createT := createUHostTest{ - expectedOutRegexp: regexp.MustCompile(`uhost\[([\w-]+)\] is initializing...done`), +type stopUHostTest struct { + flags []string + uhostIDs []string + expectedOutRegexp *regexp.Regexp +} + +func (test *stopUHostTest) run(t *testing.T) { + buf := new(bytes.Buffer) + cmd := NewCmdUHostStop(buf) + cmd.Flags().Parse(test.flags) + + if err := cmd.Execute(); err != nil { + t.Fatalf("unexpected error executing command: %v, flags: %v", err, test.flags) + } + list := test.expectedOutRegexp.FindStringSubmatch(buf.String()) + if list == nil { + t.Errorf("unexpect output:%s", buf.String()) + } +} + +type startUHostTest struct { + flags []string + uhostIDs []string + expectedOutRegexp *regexp.Regexp +} + +func (test *startUHostTest) run(t *testing.T) { + buf := new(bytes.Buffer) + cmd := NewCmdUHostStart(buf) + cmd.Flags().Parse(test.flags) + + if err := cmd.Execute(); err != nil { + t.Fatalf("unexpected error executing command: %v, flags: %v", err, test.flags) + } + list := test.expectedOutRegexp.FindStringSubmatch(buf.String()) + if list == nil { + t.Errorf("unexpect output:%s", buf.String()) + } +} + +type restartUHostTest struct { + flags []string + uhostIDs []string + expectedOutRegexp *regexp.Regexp +} + +func (test *restartUHostTest) run(t *testing.T) { + buf := new(bytes.Buffer) + cmd := NewCmdUHostReboot(buf) + cmd.Flags().Parse(test.flags) + + if err := cmd.Execute(); err != nil { + t.Fatalf("unexpected error executing command: %v, flags: %v", err, test.flags) + } + list := test.expectedOutRegexp.FindStringSubmatch(buf.String()) + if list == nil { + t.Errorf("unexpect output:%s", buf.String()) + } +} + +type poweroffUHostTest struct { + flags []string + uhostIDs []string + expectedOutRegexp *regexp.Regexp +} + +func (test *poweroffUHostTest) run(t *testing.T) { + buf := new(bytes.Buffer) + cmd := NewCmdUHostPoweroff(buf) + cmd.Flags().Parse(test.flags) + + if err := cmd.Execute(); err != nil { + t.Fatalf("unexpected error executing command: %v, flags: %v", err, test.flags) + } + list := test.expectedOutRegexp.FindStringSubmatch(buf.String()) + if list == nil { + t.Errorf("unexpect output:%s", buf.String()) + } +} + +func TestUhost(t *testing.T) { + listImageT := listImageTest{ + flags: []string{"--json"}, + } + imageID := listImageT.run(t) + + createT := createUHostTest{expectedOutRegexp: regexp.MustCompile(`uhost\[([\w-]+)\] is initializing\.\.\.done`), flags: []string{ "--cpu=1", "--memory-gb=1", - "--image-id=uimage-aaee5e", - "--password=test.lxj", + "--image-id=" + imageID, + "--password=testlxj@123", }, } createT.run(t) + restartT := restartUHostTest{ + flags: []string{fmt.Sprintf("--uhost-id=%s", strings.Join(createT.uhostIDs, ","))}, + expectedOutRegexp: regexp.MustCompile(`uhost\[([\w-]+)\] is restarting\.\.\.done`), + } + restartT.run(t) + + poweroffT := poweroffUHostTest{ + flags: []string{"--yes", fmt.Sprintf("--uhost-id=%s", strings.Join(createT.uhostIDs, ","))}, + expectedOutRegexp: regexp.MustCompile(`uhost\[([\w-]+)\] is power off`), + } + poweroffT.run(t) + + time.Sleep(time.Second * 5) + startT := startUHostTest{ + flags: []string{fmt.Sprintf("--uhost-id=%s", strings.Join(createT.uhostIDs, ","))}, + expectedOutRegexp: regexp.MustCompile(`uhost\[([\w-]+)\] is starting\.\.\.done`), + } + startT.run(t) + + stopT := stopUHostTest{ + flags: []string{fmt.Sprintf("--uhost-id=%s", strings.Join(createT.uhostIDs, ","))}, + expectedOutRegexp: regexp.MustCompile(`uhost\[([\w-]+)\] is shutting down\.\.\.done`), + } + stopT.run(t) + stopT.run(t) + deleteT := deleteUHostTest{ - uhostIds: createT.uhostIds, - expectedOutRegexp: regexp.MustCompile(`uhost:\[[w-]+\] deleted`), + uhostIDs: createT.uhostIDs, + expectedOutRegexp: regexp.MustCompile(`uhost\[([\w-]+)\] deleted`), flags: []string{"--yes"}, } - deleteT.flags = append(deleteT.flags, fmt.Sprintf("--uhost-id=%s", strings.Join(deleteT.uhostIds, ","))) + deleteT.flags = append(deleteT.flags, fmt.Sprintf("--uhost-id=%s", strings.Join(deleteT.uhostIDs, ","))) deleteT.run(t) + } diff --git a/cmd/ulb.go b/cmd/ulb.go index fb677d486c..0fb8b20de5 100644 --- a/cmd/ulb.go +++ b/cmd/ulb.go @@ -38,7 +38,7 @@ func NewCmdULB() *cobra.Command { } out := base.Cxt.GetWriter() - cmd.AddCommand(NewCmdULBList()) + cmd.AddCommand(NewCmdULBList(out)) cmd.AddCommand(NewCmdULBCreate(out)) cmd.AddCommand(NewCmdULBUpdate(out)) cmd.AddCommand(NewCmdULBDelete(out)) @@ -60,7 +60,7 @@ type ULBRow struct { } //NewCmdULBList ucloud ulb list -func NewCmdULBList() *cobra.Command { +func NewCmdULBList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeULBRequest() cmd := &cobra.Command{ Use: "list", @@ -95,7 +95,7 @@ func NewCmdULBList() *cobra.Command { list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } @@ -417,7 +417,7 @@ func NewCmdULBVServerList(out io.Writer) *cobra.Command { row.HealthCheckPath = vs.Path list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } @@ -721,7 +721,7 @@ func NewCmdULBVServerListNode(out io.Writer) *cobra.Command { } list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } flags := cmd.Flags() @@ -1036,7 +1036,7 @@ func NewCmdULBVServerListPolicy(out io.Writer) *cobra.Command { row.Backends = strings.Join(nodes, ",") list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) } }, } @@ -1217,7 +1217,7 @@ func NewCmdULBSSL() *cobra.Command { Long: "List and manipulate SSL Certificates for ULB", } out := base.Cxt.GetWriter() - cmd.AddCommand(NewCmdSSLList()) + cmd.AddCommand(NewCmdSSLList(out)) cmd.AddCommand(NewCmdSSLDescribe(out)) cmd.AddCommand(NewCmdSSLAdd(out)) cmd.AddCommand(NewCmdSSLDelete(out)) @@ -1236,7 +1236,7 @@ type SSLCertificate struct { } //NewCmdSSLList ucloud ulb-ssl-certificate list -func NewCmdSSLList() *cobra.Command { +func NewCmdSSLList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeSSLRequest() cmd := &cobra.Command{ Use: "list", @@ -1264,7 +1264,7 @@ func NewCmdSSLList() *cobra.Command { row.BindResource = strings.Join(targets, ",") rows = append(rows, row) } - base.PrintList(rows) + base.PrintList(rows, out) }, } flags := cmd.Flags() diff --git a/cmd/umem.go b/cmd/umem.go index c0bd2347bd..18fadb9b52 100644 --- a/cmd/umem.go +++ b/cmd/umem.go @@ -34,7 +34,7 @@ func NewCmdRedis() *cobra.Command { Long: "List and manipulate redis instances", } out := base.Cxt.GetWriter() - cmd.AddCommand(NewCmdRedisList()) + cmd.AddCommand(NewCmdRedisList(out)) cmd.AddCommand(NewCmdRedisCreate(out)) cmd.AddCommand(NewCmdRedisDelete(out)) return cmd @@ -48,7 +48,7 @@ func NewCmdMemcache() *cobra.Command { Long: "List and manipulate memcache instances", } out := base.Cxt.GetWriter() - cmd.AddCommand(NewCmdMemcacheList()) + cmd.AddCommand(NewCmdMemcacheList(out)) cmd.AddCommand(NewCmdMemcacheCreate(out)) cmd.AddCommand(NewCmdMemcacheDelete(out)) return cmd @@ -75,7 +75,7 @@ var redisTypeMap = map[string]string{ } //NewCmdRedisList ucloud redis list -func NewCmdRedisList() *cobra.Command { +func NewCmdRedisList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeUMemRequest() cmd := &cobra.Command{ Use: "list", @@ -124,7 +124,7 @@ func NewCmdRedisList() *cobra.Command { list = append(list, srow) } } - base.PrintList(list) + base.PrintList(list, out) }, } @@ -302,7 +302,7 @@ type UMemMemcacheRow struct { } //NewCmdMemcacheList ucloud memcache list -func NewCmdMemcacheList() *cobra.Command { +func NewCmdMemcacheList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeUMemcacheGroupRequest() cmd := &cobra.Command{ Use: "list", @@ -328,7 +328,7 @@ func NewCmdMemcacheList() *cobra.Command { } list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } diff --git a/cmd/unet.go b/cmd/unet.go index 92eb281006..7a338486de 100644 --- a/cmd/unet.go +++ b/cmd/unet.go @@ -133,7 +133,7 @@ func NewCmdUDPNList(out io.Writer) *cobra.Command { row.CreationTime = base.FormatDate(udpn.CreateTime) list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } diff --git a/cmd/uphost.go b/cmd/uphost.go index 2fa4bbb446..e9b4b02a68 100644 --- a/cmd/uphost.go +++ b/cmd/uphost.go @@ -16,6 +16,7 @@ package cmd import ( "fmt" + "io" "github.com/spf13/cobra" "github.com/ucloud/ucloud-cli/base" @@ -29,7 +30,8 @@ func NewCmdUPHost() *cobra.Command { Long: `List UPHost instances`, Args: cobra.NoArgs, } - cmd.AddCommand(NewCmdUPHostList()) + out := base.Cxt.GetWriter() + cmd.AddCommand(NewCmdUPHostList(out)) return cmd } @@ -47,7 +49,7 @@ type uphostRow struct { } //NewCmdUPHostList ucloud uphost list -func NewCmdUPHostList() *cobra.Command { +func NewCmdUPHostList(out io.Writer) *cobra.Command { ids := []string{} req := base.BizClient.NewDescribePHostRequest() cmd := &cobra.Command{ @@ -85,7 +87,7 @@ func NewCmdUPHostList() *cobra.Command { } list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } flags := cmd.Flags() diff --git a/cmd/vpc.go b/cmd/vpc.go index 9042f1bd8f..4d79c6d03d 100644 --- a/cmd/vpc.go +++ b/cmd/vpc.go @@ -23,12 +23,12 @@ func NewCmdVpc() *cobra.Command { Long: "List and manipulate VPC instances", Args: cobra.NoArgs, } - + out := base.Cxt.GetWriter() cmd.AddCommand(NewCmdVpcCreate()) - cmd.AddCommand(NewCmdVPCList()) + cmd.AddCommand(NewCmdVPCList(out)) cmd.AddCommand(NewCmdVpcDelete()) cmd.AddCommand(NewCmdVpcCreatePeer()) - cmd.AddCommand(NewCmdVpcListPeer()) + cmd.AddCommand(NewCmdVpcListPeer(out)) cmd.AddCommand(NewCmdVpcDeletePeer()) return cmd } @@ -44,7 +44,7 @@ type VPCRow struct { } //NewCmdVPCList ucloud vpc list -func NewCmdVPCList() *cobra.Command { +func NewCmdVPCList(out io.Writer) *cobra.Command { vpcIDs := []string{} req := base.BizClient.NewDescribeVPCRequest() cmd := &cobra.Command{ @@ -72,7 +72,7 @@ func NewCmdVPCList() *cobra.Command { row.CreationTime = base.FormatDate(vpc.CreateTime) list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } flags := cmd.Flags() @@ -225,7 +225,7 @@ type VPCIntercomRow struct { } //NewCmdVpcListPeer ucloud vpc list-intercome -func NewCmdVpcListPeer() *cobra.Command { +func NewCmdVpcListPeer(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeVPCIntercomRequest() cmd := &cobra.Command{ Use: "list-intercome", @@ -250,7 +250,7 @@ func NewCmdVpcListPeer() *cobra.Command { row.Group = VPCIntercom.Tag list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } req.VPCId = cmd.Flags().String("vpc-id", "", "Required. The vpc id which you wnat to describe the information") @@ -340,7 +340,7 @@ func NewCmdSubnet() *cobra.Command { Args: cobra.NoArgs, } out := base.Cxt.GetWriter() - cmd.AddCommand(NewCmdSubnetList()) + cmd.AddCommand(NewCmdSubnetList(out)) cmd.AddCommand(NewCmdSubnetCreate()) cmd.AddCommand(NewCmdSubnetDelete(out)) cmd.AddCommand(NewCmdSubnetListResource(out)) @@ -359,7 +359,7 @@ type SubnetRow struct { } //NewCmdSubnetList ucloud subnet list -func NewCmdSubnetList() *cobra.Command { +func NewCmdSubnetList(out io.Writer) *cobra.Command { req := base.BizClient.NewDescribeSubnetRequest() cmd := &cobra.Command{ Use: "list", @@ -383,7 +383,7 @@ func NewCmdSubnetList() *cobra.Command { row.CreationTime = base.FormatDate(sn.CreateTime) list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } @@ -516,7 +516,7 @@ func NewCmdSubnetListResource(out io.Writer) *cobra.Command { } list = append(list, row) } - base.PrintList(list) + base.PrintList(list, out) }, } flags := cmd.Flags() diff --git a/go.mod b/go.mod index 2e08689b8e..9cfdedb8b4 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.3 - github.com/ucloud/ucloud-sdk-go v0.8.1 + github.com/ucloud/ucloud-sdk-go v0.8.2 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 // indirect golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect diff --git a/go.sum b/go.sum index f3ca25a370..4e0daa76c7 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,8 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/ucloud/ucloud-sdk-go v0.8.1 h1:DSpOp7avxkmTjtwnD+tdcEHSMTRNH/5+2rQnjJl+198= -github.com/ucloud/ucloud-sdk-go v0.8.1/go.mod h1:lM6fpI8y6iwACtlbHUav823/uKPdXsNBlnBpRF2fj3c= +github.com/ucloud/ucloud-sdk-go v0.8.2 h1:mMSPTIh7L6XZjOBpIlHuRasdQLrbjtqs1LaxKYQ4Uqg= +github.com/ucloud/ucloud-sdk-go v0.8.2/go.mod h1:lM6fpI8y6iwACtlbHUav823/uKPdXsNBlnBpRF2fj3c= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= diff --git a/ux/document.go b/ux/document.go index 14cc3a7625..59fd7ec2d6 100644 --- a/ux/document.go +++ b/ux/document.go @@ -11,14 +11,16 @@ import ( "github.com/ucloud/ucloud-cli/ansi" ) +var width, rows, _ = terminalSize() + //Document 当前进程在打印的内容 type document struct { blocks []*Block + mux sync.RWMutex framesPerSecond int once sync.Once out io.Writer ticker *time.Ticker - mux sync.Mutex ctx context.Context cancel context.CancelFunc allBlockFull chan bool @@ -26,13 +28,13 @@ type document struct { disable bool } -var width, rows, _ = terminalSize() - func (d *document) reset() { size := 0 + d.mux.RLock() for _, block := range d.blocks { size += block.printLineNum } + d.mux.RUnlock() if size != 0 { fmt.Printf(ansi.CursorLeft + ansi.CursorPrevLine(size) + ansi.EraseDown) } @@ -42,6 +44,20 @@ func (d *document) Disable() { d.disable = true } +func (d *document) SetWriter(out io.Writer) { + d.out = out +} + +func (d *document) Content() []string { + var lines []string + for _, block := range d.blocks { + for _, line := range block.lines { + lines = append(lines, line) + } + } + return lines +} + func (d *document) Render() { if d.disable { return @@ -50,6 +66,7 @@ func (d *document) Render() { go func() { for range d.ticker.C { d.reset() + d.mux.RLock() for _, block := range d.blocks { block.printLineNum = 0 block.mux.Lock() @@ -66,6 +83,7 @@ func (d *document) Render() { fmt.Fprintf(d.out, "\n") block.printLineNum++ } + d.mux.RUnlock() } }() go d.checkBlockDone() @@ -88,7 +106,10 @@ func (d *document) checkBlockFull(ctx context.Context) { allFull := make(chan struct{}) go func() { for _, b := range d.blocks { - <-b.full + select { + case <-b.full: + case <-ctx.Done(): + } } close(allFull) }() @@ -139,7 +160,7 @@ type Block struct { mux sync.Mutex lines []string stable chan struct{} //标识此块已稳定,不再轮询 - full chan struct{} //标识此块不再添加新的内容 + full chan struct{} //标识此块不再添加新的内容, 一般轮询完成后才标识为Full, 不太合理,fixme } //Update lines in Block From 2af77c6da2a85bf48fdf64a10843aad06b865d3b Mon Sep 17 00:00:00 2001 From: lixiaojun Date: Fri, 19 Apr 2019 13:54:30 +0800 Subject: [PATCH 2/2] bugfix & update version --- Makefile | 2 +- base/config.go | 2 +- cmd/uhost.go | 27 +++++++++++---------------- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index ac21fc6527..51280997dd 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -export VERSION=0.1.14 +export VERSION=0.1.15 .PHONY : build build: diff --git a/base/config.go b/base/config.go index 6bfc81b960..5998339d8e 100644 --- a/base/config.go +++ b/base/config.go @@ -30,7 +30,7 @@ const DefaultBaseURL = "https://api.ucloud.cn/" const DefaultProfile = "default" //Version 版本号 -const Version = "0.1.14" +const Version = "0.1.15" //ConfigIns 配置实例, 程序加载时生成 var ConfigIns = &AggConfig{} diff --git a/cmd/uhost.go b/cmd/uhost.go index 05584aff9c..300bd3b86b 100644 --- a/cmd/uhost.go +++ b/cmd/uhost.go @@ -162,18 +162,11 @@ func NewCmdUHostCreate() *cobra.Command { if len(bindEipIDs) > i { bindEipID = bindEipIDs[i] } - go createUhostWrapper(req, eipReq, bindEipID, async, make(chan bool, count), wg, tokens) + go createUhostWrapper(req, eipReq, bindEipID, async, make(chan bool, count), wg, tokens, i) } } else { retCh := make(chan bool, count) ux.Doc.Disable() - - result := map[string]int{ - "total": count, - "success": 0, - "fail": 0, - } - refresh := ux.NewRefresh() go func() { @@ -182,20 +175,21 @@ func NewCmdUHostCreate() *cobra.Command { if len(bindEipIDs) > i { bindEipID = bindEipIDs[i] } - go createUhostWrapper(req, eipReq, bindEipID, async, retCh, wg, tokens) + go createUhostWrapper(req, eipReq, bindEipID, async, retCh, wg, tokens, i) } }() go func() { - refresh.Do(fmt.Sprintf("uhost creating, total:%d, success:%d, fail:%d", result["total"], result["success"], result["fail"])) + var success, fail int + refresh.Do(fmt.Sprintf("uhost creating, total:%d, success:%d, fail:%d", count, success, fail)) for ret := range retCh { if ret { - result["success"]++ + success++ } else { - result["fail"]++ + fail++ } - refresh.Do(fmt.Sprintf("uhost creating, total:%d, success:%d, fail:%d", result["total"], result["success"], result["fail"])) - if result["total"] == result["success"]+result["fail"] && result["fail"] > 0 { + refresh.Do(fmt.Sprintf("uhost creating, total:%d, success:%d, fail:%d", count, success, fail)) + if count == success+fail && fail > 0 { fmt.Printf("Check logs in %s\n", base.GetLogFilePath()) } } @@ -292,7 +286,7 @@ func NewCmdUHostCreate() *cobra.Command { } //createUhostWrapper 处理UI和并发控制 -func createUhostWrapper(req *uhost.CreateUHostInstanceRequest, eipReq *unet.AllocateEIPRequest, bindEipID string, async bool, retCh chan<- bool, wg *sync.WaitGroup, tokens chan struct{}) { +func createUhostWrapper(req *uhost.CreateUHostInstanceRequest, eipReq *unet.AllocateEIPRequest, bindEipID string, async bool, retCh chan<- bool, wg *sync.WaitGroup, tokens chan struct{}, idx int) { //控制并发数量 tokens <- struct{}{} defer func() { @@ -301,8 +295,9 @@ func createUhostWrapper(req *uhost.CreateUHostInstanceRequest, eipReq *unet.Allo }() success, logs := createUhost(req, eipReq, bindEipID, async) - base.Log(logs) retCh <- success + logs = append(logs, fmt.Sprintf("index:%d, result:%t", idx, success)) + base.Log(logs) } func createUhost(req *uhost.CreateUHostInstanceRequest, eipReq *unet.AllocateEIPRequest, bindEipID string, async bool) (bool, []string) {