From 42b49df4ac957a684a799c82f08eb6c5f68c4e05 Mon Sep 17 00:00:00 2001 From: "qingping.fang" Date: Wed, 17 Jun 2026 16:40:43 +0800 Subject: [PATCH 1/4] Update the new architecture interface of the umem (#94) --- cmd/umem.go | 388 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 294 insertions(+), 94 deletions(-) diff --git a/cmd/umem.go b/cmd/umem.go index 3964e9af8a..3dd7ac0f08 100644 --- a/cmd/umem.go +++ b/cmd/umem.go @@ -22,6 +22,7 @@ import ( "github.com/spf13/cobra" "github.com/ucloud/ucloud-sdk-go/services/umem" + "github.com/ucloud/ucloud-sdk-go/services/vpc" sdk "github.com/ucloud/ucloud-sdk-go/ucloud" "github.com/ucloud/ucloud-sdk-go/ucloud/request" @@ -152,51 +153,47 @@ func NewCmdRedisList(out io.Writer) *cobra.Command { return cmd } +// redisCreateParams holds the shared flag values for redis create commands. +type redisCreateParams struct { + name string + password string + size int + region string + zone string + projectID string + chargeType string + quantity int + group string + vpcID string + subnetID string + version string + blockCnt int + proxySize int +} + // NewCmdRedisCreate ucloud redis create func NewCmdRedisCreate(out io.Writer) *cobra.Command { - req := base.BizClient.NewCreateURedisGroupRequest() - req.HighAvailability = sdk.String("enable") - var redisType, password string + var redisType string + var p redisCreateParams cmd := &cobra.Command{ Use: "create", Short: "Create redis instance", Long: "Create redis instance", Run: func(c *cobra.Command, args []string) { - if l := len(*req.Name); l < 6 || l > 63 { + if l := len(p.name); l < 6 || l > 63 { fmt.Fprintln(out, "length of name should be between 6 and 63") return } - if password != "" { - req.Password = &password + if err := fillDefaultVPCAndSubnet(&p.vpcID, &p.subnetID, p.projectID, p.region, p.zone); err != nil { + fmt.Fprintln(out, err) + return } - if redisType == "master-replica" { - resp, err := base.BizClient.CreateURedisGroup(req) - if err != nil { - base.HandleError(err) - return - } - fmt.Printf("redis[%s] created\n", resp.GroupId) - } else if redisType == "distributed" { - dreq := base.BizClient.NewCreateUMemSpaceRequest() - dreq.Region = req.Region - dreq.Zone = req.Zone - dreq.ProjectId = req.ProjectId - dreq.Name = req.Name - dreq.Size = req.Size - if *req.Size == 1 { - dreq.Size = sdk.Int(16) - } - dreq.ChargeType = req.ChargeType - dreq.Quantity = req.Quantity - dreq.Tag = req.Tag - dreq.Password = req.Password - resp, err := base.BizClient.CreateUMemSpace(dreq) - if err != nil { - base.HandleError(err) - return - } - fmt.Printf("redis[%s] created\n", resp.SpaceId) - } else { + switch redisType { + case "master-replica": + createMasterReplicaRedis(out, &p) + case "distributed": + createDistributedRedis(out, &p) + default: fmt.Printf("unknow redis type[%s], it's should be 'master-replica' or 'distributed'\n", redisType) } }, @@ -205,28 +202,41 @@ func NewCmdRedisCreate(out io.Writer) *cobra.Command { flags := cmd.Flags() flags.SortFlags = false - req.Name = flags.String("name", "", "Required. Name of the redis to create. Range of the password length is [6,63] and the password can only contain letters and numbers") + flags.StringVar(&p.name, "name", "", "Required. Name of the redis to create.") flags.StringVar(&redisType, "type", "", "Required. Type of the redis. Accept values:'master-replica','distributed'") - req.Size = flags.Int("size-gb", 1, "Optional. Memory size. Default value 1GB(for master-replica redis type) or 16GB(for distributed redis type). Unit GB") - req.Version = flags.String("version", "3.2", "Optional. Version of redis") - req.VPCId = flags.String("vpc-id", "", "Optional. VPC ID. This field is required under VPC2.0. See 'ucloud vpc list'") - req.SubnetId = flags.String("subnet-id", "", "Optional. Subnet ID. This field is required under VPC2.0. See 'ucloud subnet list'") - flags.StringVar(&password, "password", "", "Optional. Password of redis to create") - - bindRegion(req, flags) - bindZone(req, flags) - bindProjectID(req, flags) - bindGroup(req, flags) - bindChargeType(req, flags) - bindQuantity(req, flags) - - flags.SetFlagValues("version", "3.0", "3.2", "4.0") + flags.IntVar(&p.size, "size-gb", 1, "Optional. Memory size. Default value 1GB(for master-replica redis type) or block-cnt*1GB(for distributed redis type). Unit GB") + flags.StringVar(&p.version, "version", "6.0", "Optional. Version of redis. Accept values: '4.0', '5.0', '6.0', '7.0'") + flags.StringVar(&p.vpcID, "vpc-id", "", "Optional. VPC ID. This field is required under VPC2.0. See 'ucloud vpc list'") + flags.StringVar(&p.subnetID, "subnet-id", "", "Optional. Subnet ID. This field is required under VPC2.0. See 'ucloud subnet list'") + flags.StringVar(&p.password, "password", "", "Optional. Password of redis to create. Range of the password length is [6,63] and the password can only contain letters and numbers") + + //distributed optional params + flags.IntVar(&p.blockCnt, "block-cnt", 2, "Optional. Block count. Default value 2(for distributed redis type).") + flags.IntVar(&p.proxySize, "proxy-size", 2, "Optional. Proxy size. Default value 2(for distributed redis type) Unit Core") + + p.region = base.ConfigIns.Region + flags.StringVar(&p.region, "region", base.ConfigIns.Region, "Optional. Override default region for this command invocation, see 'ucloud region'") + p.zone = base.ConfigIns.Zone + flags.StringVar(&p.zone, "zone", base.ConfigIns.Zone, "Optional. Override default availability zone for this command invocation, see 'ucloud region'") + p.projectID = base.ConfigIns.ProjectID + flags.StringVar(&p.projectID, "project-id", base.ConfigIns.ProjectID, "Optional. Override default project-id for this command invocation, see 'ucloud project list'") + flags.StringVar(&p.chargeType, "charge-type", "Month", "Optional. Enumeration value.'Year',pay yearly;'Month',pay monthly; 'Dynamic', pay hourly; 'Trial', free trial(need permission)") + flags.IntVar(&p.quantity, "quantity", 1, "Optional. The duration of the instance. N years/months.") + flags.StringVar(&p.group, "group", "", "Optional. Business group") + + flags.SetFlagValues("version", "4.0", "5.0", "6.0", "7.0") flags.SetFlagValues("type", "master-replica", "distributed") + flags.SetFlagValues("charge-type", "Month", "Dynamic", "Year") + flags.SetFlagValuesFunc("region", getRegionList) + flags.SetFlagValuesFunc("zone", func() []string { + return getZoneList(p.region) + }) + flags.SetFlagValuesFunc("project-id", getProjectList) flags.SetFlagValuesFunc("vpc-id", func() []string { - return getAllVPCIdNames(*req.ProjectId, *req.Region) + return getAllVPCIdNames(p.projectID, p.region) }) flags.SetFlagValuesFunc("subnet-id", func() []string { - return getAllSubnetIDNames(*req.VPCId, *req.ProjectId, *req.Region) + return getAllSubnetIDNames(p.vpcID, p.projectID, p.region) }) cmd.MarkFlagRequired("name") @@ -235,10 +245,81 @@ func NewCmdRedisCreate(out io.Writer) *cobra.Command { return cmd } +func createMasterReplicaRedis(out io.Writer, p *redisCreateParams) { + req := base.BizClient.NewCreateURedisGroupRequest() + req.Region = &p.region + req.Zone = &p.zone + req.ProjectId = &p.projectID + req.Name = &p.name + req.HighAvailability = sdk.String("enable") + req.Size = &p.size + req.Version = &p.version + req.VPCId = &p.vpcID + req.SubnetId = &p.subnetID + req.ChargeType = &p.chargeType + req.Quantity = &p.quantity + req.Tag = &p.group + if p.password != "" { + req.Password = &p.password + } + + resp, err := base.BizClient.CreateURedisGroup(req) + if err != nil { + base.HandleError(err) + return + } + fmt.Fprintf(out, "redis[%s] created\n", resp.GroupId) +} + +func createDistributedRedis(out io.Writer, p *redisCreateParams) { + req := base.BizClient.NewCreateUMemSpaceRequest() + req.Region = &p.region + req.Zone = &p.zone + req.ProjectId = &p.projectID + req.Name = &p.name + req.Protocol = sdk.String("redis") + req.ProxySize = &p.proxySize + if p.blockCnt <= 0 { + fmt.Fprintln(out, "block-cnt should be greater than 0") + return + } + req.BlockCnt = &p.blockCnt + + size := p.size + if size < p.blockCnt || size%p.blockCnt != 0 { + size = p.blockCnt + } + + req.Size = &size + req.Version = &p.version + req.VPCId = &p.vpcID + req.SubnetId = &p.subnetID + req.ChargeType = &p.chargeType + req.Quantity = &p.quantity + req.Tag = &p.group + if p.password != "" { + req.Password = &p.password + } + + resp, err := base.BizClient.CreateUMemSpace(req) + if err != nil { + base.HandleError(err) + return + } + fmt.Fprintf(out, "redis[%s] created\n", resp.SpaceId) +} + +// redisDeleteParams holds the shared flag values for redis delete commands. +type redisDeleteParams struct { + region string + zone string + projectID string +} + // NewCmdRedisDelete ucloud redis delete func NewCmdRedisDelete(out io.Writer) *cobra.Command { var idNames []string - req := base.BizClient.NewDeleteURedisGroupRequest() + var p redisDeleteParams cmd := &cobra.Command{ Use: "delete", Short: "Delete redis instances", @@ -247,26 +328,13 @@ func NewCmdRedisDelete(out io.Writer) *cobra.Command { Run: func(c *cobra.Command, args []string) { for _, idname := range idNames { id := base.PickResourceID(idname) - if strings.HasPrefix(id, "uredis") { - req.GroupId = &id - _, err := base.BizClient.DeleteURedisGroup(req) - if err != nil { - base.HandleError(err) - continue - } - } else if strings.HasPrefix(id, "umem") { - _req := base.BizClient.NewDeleteUMemSpaceRequest() - _req.Region = req.Region - _req.Zone = req.Zone - _req.ProjectId = req.ProjectId - _req.SpaceId = &id - _, err := base.BizClient.DeleteUMemSpace(_req) - if err != nil { - base.HandleError(err) - continue - } + if strings.HasPrefix(id, "uredis") || strings.HasPrefix(id, "uhredis") { + deleteMasterReplicaRedis(out, &p, id) + } else if strings.HasPrefix(id, "udredis") { + deleteDistributedRedis(out, &p, id) + } else { + fmt.Fprintf(out, "redis[%s] unknown id prefix, skip\n", idname) } - fmt.Fprintf(out, "redis[%s] deleted\n", idname) } }, } @@ -274,20 +342,56 @@ func NewCmdRedisDelete(out io.Writer) *cobra.Command { flags := cmd.Flags() flags.SortFlags = false - flags.StringSliceVar(&idNames, "umem-id", nil, "Required. Resource ID of redis intances to delete") - bindProjectID(req, flags) - bindRegion(req, flags) - bindZone(req, flags) + flags.StringSliceVar(&idNames, "umem-id", nil, "Required. Resource ID of redis instances to delete") + p.region = base.ConfigIns.Region + flags.StringVar(&p.region, "region", base.ConfigIns.Region, "Optional. Override default region for this command invocation, see 'ucloud region'") + p.zone = base.ConfigIns.Zone + flags.StringVar(&p.zone, "zone", base.ConfigIns.Zone, "Optional. Override default availability zone for this command invocation, see 'ucloud region'") + p.projectID = base.ConfigIns.ProjectID + flags.StringVar(&p.projectID, "project-id", base.ConfigIns.ProjectID, "Optional. Override default project-id for this command invocation, see 'ucloud project list'") cmd.MarkFlagRequired("umem-id") flags.SetFlagValuesFunc("umem-id", func() []string { - return getRedisIDList(*req.ProjectId, *req.Region) + return getRedisIDList(p.projectID, p.region) }) + flags.SetFlagValuesFunc("region", getRegionList) + flags.SetFlagValuesFunc("zone", func() []string { + return getZoneList(p.region) + }) + flags.SetFlagValuesFunc("project-id", getProjectList) return cmd } +func deleteMasterReplicaRedis(out io.Writer, p *redisDeleteParams, id string) { + req := base.BizClient.NewDeleteURedisGroupRequest() + req.Region = &p.region + req.Zone = &p.zone + req.ProjectId = &p.projectID + req.GroupId = &id + _, err := base.BizClient.DeleteURedisGroup(req) + if err != nil { + base.HandleError(err) + return + } + fmt.Fprintf(out, "redis[%s] deleted\n", id) +} + +func deleteDistributedRedis(out io.Writer, p *redisDeleteParams, id string) { + req := base.BizClient.NewDeleteUMemSpaceRequest() + req.Region = &p.region + req.Zone = &p.zone + req.ProjectId = &p.projectID + req.SpaceId = &id + _, err := base.BizClient.DeleteUMemSpace(req) + if err != nil { + base.HandleError(err) + return + } + fmt.Fprintf(out, "redis[%s] deleted\n", id) +} + // NewCmdRedisRestart ucloud redis restart func NewCmdRedisRestart(out io.Writer) *cobra.Command { idNames := make([]string, 0) @@ -429,54 +533,97 @@ func NewCmdMemcacheList(out io.Writer) *cobra.Command { return cmd } +// memcacheCreateParams holds the shared flag values for memcache create commands. +type memcacheCreateParams struct { + name string + size int + region string + zone string + projectID string + chargeType string + quantity int + group string + vpcID string + subnetID string +} + // NewCmdMemcacheCreate ucloud memcache create func NewCmdMemcacheCreate(out io.Writer) *cobra.Command { - req := base.BizClient.NewCreateUMemcacheGroupRequest() + var p memcacheCreateParams cmd := &cobra.Command{ Use: "create", Short: "Create memcache instance", Long: "Create memcache instance", Run: func(c *cobra.Command, args []string) { - if *req.Size > 32 || *req.Size < 1 { + if p.size > 32 || p.size < 1 { fmt.Fprintln(out, "size-gb should be between 1 and 32") return } - resp, err := base.BizClient.CreateUMemcacheGroup(req) - if err != nil { - base.HandleError(err) + if err := fillDefaultVPCAndSubnet(&p.vpcID, &p.subnetID, p.projectID, p.region, p.zone); err != nil { + fmt.Fprintln(out, err) return } - fmt.Fprintf(out, "memcache[%s] created\n", resp.GroupId) + createMemcache(out, &p) }, } flags := cmd.Flags() flags.SortFlags = false - req.Name = flags.String("name", "", "Required. Name of memcache instance to create") - req.Size = flags.Int("size-gb", 1, "Optional. Memory size of memcache instance. Unit GB. Accpet values:1,2,4,8,16,32") - req.VPCId = flags.String("vpc-id", "", "Optional. VPC ID. See 'ucloud vpc list'") - req.SubnetId = flags.String("subnet-id", "", "Optional. Subnet ID. See 'ucloud subnet list'") - bindProjectID(req, flags) - bindRegion(req, flags) - bindZone(req, flags) - bindChargeType(req, flags) - bindQuantity(req, flags) - bindGroup(req, flags) + flags.StringVar(&p.name, "name", "", "Required. Name of memcache instance to create") + flags.IntVar(&p.size, "size-gb", 1, "Optional. Memory size of memcache instance. Unit GB. Accpet values:1,2,4,8,16,32") + flags.StringVar(&p.vpcID, "vpc-id", "", "Optional. VPC ID. See 'ucloud vpc list'") + flags.StringVar(&p.subnetID, "subnet-id", "", "Optional. Subnet ID. See 'ucloud subnet list'") + p.region = base.ConfigIns.Region + flags.StringVar(&p.region, "region", base.ConfigIns.Region, "Optional. Override default region for this command invocation, see 'ucloud region'") + p.zone = base.ConfigIns.Zone + flags.StringVar(&p.zone, "zone", base.ConfigIns.Zone, "Optional. Override default availability zone for this command invocation, see 'ucloud region'") + p.projectID = base.ConfigIns.ProjectID + flags.StringVar(&p.projectID, "project-id", base.ConfigIns.ProjectID, "Optional. Override default project-id for this command invocation, see 'ucloud project list'") + flags.StringVar(&p.chargeType, "charge-type", "Month", "Optional. Enumeration value.'Year',pay yearly;'Month',pay monthly; 'Dynamic', pay hourly; 'Trial', free trial(need permission)") + flags.IntVar(&p.quantity, "quantity", 1, "Optional. The duration of the instance. N years/months.") + flags.StringVar(&p.group, "group", "", "Optional. Business group") flags.SetFlagValues("size-gb", "1", "2", "4", "8", "16", "32") + flags.SetFlagValues("charge-type", "Month", "Dynamic", "Year") flags.SetFlagValuesFunc("vpc-id", func() []string { - return getAllVPCIdNames(*req.ProjectId, *req.Region) + return getAllVPCIdNames(p.projectID, p.region) }) flags.SetFlagValuesFunc("subnet-id", func() []string { - return getAllSubnetIDNames(*req.VPCId, *req.ProjectId, *req.Region) + return getAllSubnetIDNames(p.vpcID, p.projectID, p.region) }) + flags.SetFlagValuesFunc("region", getRegionList) + flags.SetFlagValuesFunc("zone", func() []string { + return getZoneList(p.region) + }) + flags.SetFlagValuesFunc("project-id", getProjectList) cmd.MarkFlagRequired("name") return cmd } +func createMemcache(out io.Writer, p *memcacheCreateParams) { + req := base.BizClient.NewCreateUMemcacheGroupRequest() + req.Region = &p.region + req.Zone = &p.zone + req.ProjectId = &p.projectID + req.Name = &p.name + req.Size = &p.size + req.VPCId = &p.vpcID + req.SubnetId = &p.subnetID + req.ChargeType = &p.chargeType + req.Quantity = &p.quantity + req.Tag = &p.group + + resp, err := base.BizClient.CreateUMemcacheGroup(req) + if err != nil { + base.HandleError(err) + return + } + fmt.Fprintf(out, "memcache[%s] created\n", resp.GroupId) +} + // NewCmdMemcacheDelete ucloud memcache delete func NewCmdMemcacheDelete(out io.Writer) *cobra.Command { var idNames []string @@ -614,6 +761,59 @@ func describeRedisByID(redisID string, commonBase *request.CommonBase) (interfac return &resp.DataSet[0], nil } +func fillDefaultVPCAndSubnet(vpcID, subnetID *string, projectID, region, zone string) error { + if *vpcID != "" && *subnetID != "" { + return nil + } + vpcs, err := getAllVPCIns(projectID, region) + if err != nil { + return fmt.Errorf("failed to get vpc list: %s", err) + } + if len(vpcs) == 0 { + return fmt.Errorf("no vpc found in region[%s], please specify --vpc-id and --subnet-id", region) + } + + // Find the default VPC + var defaultVPC *vpc.VPCInfo + for i := range vpcs { + if vpcs[i].VPCType == "DefaultVPC" { + defaultVPC = &vpcs[i] + break + } + } + // Fallback to the first VPC if no DefaultVPC found + if defaultVPC == nil { + defaultVPC = &vpcs[0] + } + + if *vpcID == "" { + *vpcID = defaultVPC.VPCId + } + + if *subnetID == "" { + subnets, err := getAllSubnets(*vpcID, projectID, region) + if err != nil { + return fmt.Errorf("failed to get subnet list: %s", err) + } + if len(subnets) == 0 { + return fmt.Errorf("no subnet found in vpc[%s], please specify --subnet-id", *vpcID) + } + // Filter subnets by zone if specified + if zone != "" { + for _, sn := range subnets { + if sn.Zone == zone { + *subnetID = sn.SubnetId + return nil + } + } + } + // Fallback to the first subnet + *subnetID = subnets[0].SubnetId + } + + return nil +} + func getMemcacheIDList(project, region string) []string { req := base.BizClient.NewDescribeUMemcacheGroupRequest() req.ProjectId = &project From aea3ad9977dff0c9f6a75f5ea31d86f5fa65ff81 Mon Sep 17 00:00:00 2001 From: "qingping.fang" Date: Wed, 17 Jun 2026 17:01:31 +0800 Subject: [PATCH 2/4] refactor(umem): simplify memcache create by removing unnecessary param struct - Remove memcacheCreateParams struct and createMemcache helper function - Use SDK request object directly since memcache only has one create type - Keep redis param structs as they are shared between master-replica and distributed types Co-Authored-By: Claude --- cmd/umem.go | 86 +++++++++++++++++++---------------------------------- 1 file changed, 31 insertions(+), 55 deletions(-) diff --git a/cmd/umem.go b/cmd/umem.go index 3dd7ac0f08..2e5023d1b4 100644 --- a/cmd/umem.go +++ b/cmd/umem.go @@ -533,68 +533,65 @@ func NewCmdMemcacheList(out io.Writer) *cobra.Command { return cmd } -// memcacheCreateParams holds the shared flag values for memcache create commands. -type memcacheCreateParams struct { - name string - size int - region string - zone string - projectID string - chargeType string - quantity int - group string - vpcID string - subnetID string -} - // NewCmdMemcacheCreate ucloud memcache create func NewCmdMemcacheCreate(out io.Writer) *cobra.Command { - var p memcacheCreateParams + req := base.BizClient.NewCreateUMemcacheGroupRequest() + var region, zone, projectID string cmd := &cobra.Command{ Use: "create", Short: "Create memcache instance", Long: "Create memcache instance", Run: func(c *cobra.Command, args []string) { - if p.size > 32 || p.size < 1 { + if *req.Size > 32 || *req.Size < 1 { fmt.Fprintln(out, "size-gb should be between 1 and 32") return } - if err := fillDefaultVPCAndSubnet(&p.vpcID, &p.subnetID, p.projectID, p.region, p.zone); err != nil { + if err := fillDefaultVPCAndSubnet(req.VPCId, req.SubnetId, *req.ProjectId, *req.Region, *req.Zone); err != nil { fmt.Fprintln(out, err) return } - createMemcache(out, &p) + resp, err := base.BizClient.CreateUMemcacheGroup(req) + if err != nil { + base.HandleError(err) + return + } + fmt.Fprintf(out, "memcache[%s] created\n", resp.GroupId) }, } flags := cmd.Flags() flags.SortFlags = false - flags.StringVar(&p.name, "name", "", "Required. Name of memcache instance to create") - flags.IntVar(&p.size, "size-gb", 1, "Optional. Memory size of memcache instance. Unit GB. Accpet values:1,2,4,8,16,32") - flags.StringVar(&p.vpcID, "vpc-id", "", "Optional. VPC ID. See 'ucloud vpc list'") - flags.StringVar(&p.subnetID, "subnet-id", "", "Optional. Subnet ID. See 'ucloud subnet list'") - p.region = base.ConfigIns.Region - flags.StringVar(&p.region, "region", base.ConfigIns.Region, "Optional. Override default region for this command invocation, see 'ucloud region'") - p.zone = base.ConfigIns.Zone - flags.StringVar(&p.zone, "zone", base.ConfigIns.Zone, "Optional. Override default availability zone for this command invocation, see 'ucloud region'") - p.projectID = base.ConfigIns.ProjectID - flags.StringVar(&p.projectID, "project-id", base.ConfigIns.ProjectID, "Optional. Override default project-id for this command invocation, see 'ucloud project list'") - flags.StringVar(&p.chargeType, "charge-type", "Month", "Optional. Enumeration value.'Year',pay yearly;'Month',pay monthly; 'Dynamic', pay hourly; 'Trial', free trial(need permission)") - flags.IntVar(&p.quantity, "quantity", 1, "Optional. The duration of the instance. N years/months.") - flags.StringVar(&p.group, "group", "", "Optional. Business group") + req.Name = flags.String("name", "", "Required. Name of memcache instance to create") + req.Size = flags.Int("size-gb", 1, "Optional. Memory size of memcache instance. Unit GB. Accpet values:1,2,4,8,16,32") + req.VPCId = flags.String("vpc-id", "", "Optional. VPC ID. See 'ucloud vpc list'") + req.SubnetId = flags.String("subnet-id", "", "Optional. Subnet ID. See 'ucloud subnet list'") + region = base.ConfigIns.Region + flags.StringVar(®ion, "region", base.ConfigIns.Region, "Optional. Override default region for this command invocation, see 'ucloud region'") + zone = base.ConfigIns.Zone + flags.StringVar(&zone, "zone", base.ConfigIns.Zone, "Optional. Override default availability zone for this command invocation, see 'ucloud region'") + projectID = base.ConfigIns.ProjectID + flags.StringVar(&projectID, "project-id", base.ConfigIns.ProjectID, "Optional. Override default project-id for this command invocation, see 'ucloud project list'") + req.ChargeType = flags.String("charge-type", "Month", "Optional. Enumeration value.'Year',pay yearly;'Month',pay monthly; 'Dynamic', pay hourly; 'Trial', free trial(need permission)") + req.Quantity = flags.Int("quantity", 1, "Optional. The duration of the instance. N years/months.") + req.Tag = flags.String("group", "", "Optional. Business group") + + // Set region/zone/projectID to request after flag parsing + req.Region = ®ion + req.Zone = &zone + req.ProjectId = &projectID flags.SetFlagValues("size-gb", "1", "2", "4", "8", "16", "32") flags.SetFlagValues("charge-type", "Month", "Dynamic", "Year") flags.SetFlagValuesFunc("vpc-id", func() []string { - return getAllVPCIdNames(p.projectID, p.region) + return getAllVPCIdNames(projectID, region) }) flags.SetFlagValuesFunc("subnet-id", func() []string { - return getAllSubnetIDNames(p.vpcID, p.projectID, p.region) + return getAllSubnetIDNames(*req.VPCId, projectID, region) }) flags.SetFlagValuesFunc("region", getRegionList) flags.SetFlagValuesFunc("zone", func() []string { - return getZoneList(p.region) + return getZoneList(region) }) flags.SetFlagValuesFunc("project-id", getProjectList) @@ -603,27 +600,6 @@ func NewCmdMemcacheCreate(out io.Writer) *cobra.Command { return cmd } -func createMemcache(out io.Writer, p *memcacheCreateParams) { - req := base.BizClient.NewCreateUMemcacheGroupRequest() - req.Region = &p.region - req.Zone = &p.zone - req.ProjectId = &p.projectID - req.Name = &p.name - req.Size = &p.size - req.VPCId = &p.vpcID - req.SubnetId = &p.subnetID - req.ChargeType = &p.chargeType - req.Quantity = &p.quantity - req.Tag = &p.group - - resp, err := base.BizClient.CreateUMemcacheGroup(req) - if err != nil { - base.HandleError(err) - return - } - fmt.Fprintf(out, "memcache[%s] created\n", resp.GroupId) -} - // NewCmdMemcacheDelete ucloud memcache delete func NewCmdMemcacheDelete(out io.Writer) *cobra.Command { var idNames []string From 85b89b5af000a6717bd74b05796a2e4a99fd3708 Mon Sep 17 00:00:00 2001 From: "qingping.fang" Date: Wed, 17 Jun 2026 18:39:50 +0800 Subject: [PATCH 3/4] refactor(umem): improve flag binding and add parameter validation - Use bindRegionS/bindZoneS/bindProjectIDS helpers to reduce code duplication - Add name validation with utf8.RuneCountInString for Chinese support - Add password validation with len() (ASCII only) - Add distributed redis validation: size must be divisible by block-cnt, proxy-size must be multiple of 2 - Update help text for name and password flags Co-Authored-By: Claude --- cmd/umem.go | 78 +++++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 42 deletions(-) diff --git a/cmd/umem.go b/cmd/umem.go index 2e5023d1b4..a667329574 100644 --- a/cmd/umem.go +++ b/cmd/umem.go @@ -18,6 +18,7 @@ import ( "fmt" "io" "strings" + "unicode/utf8" "github.com/spf13/cobra" @@ -180,10 +181,18 @@ func NewCmdRedisCreate(out io.Writer) *cobra.Command { Short: "Create redis instance", Long: "Create redis instance", Run: func(c *cobra.Command, args []string) { - if l := len(p.name); l < 6 || l > 63 { + // Validate name,support Chinese name + if l := utf8.RuneCountInString(p.name); l < 6 || l > 63 { fmt.Fprintln(out, "length of name should be between 6 and 63") return } + // Validate password + if p.password != "" { + if l := len(p.password); l < 6 || l > 36 { + fmt.Fprintln(out, "length of password should be between 6 and 36") + return + } + } if err := fillDefaultVPCAndSubnet(&p.vpcID, &p.subnetID, p.projectID, p.region, p.zone); err != nil { fmt.Fprintln(out, err) return @@ -202,24 +211,21 @@ func NewCmdRedisCreate(out io.Writer) *cobra.Command { flags := cmd.Flags() flags.SortFlags = false - flags.StringVar(&p.name, "name", "", "Required. Name of the redis to create.") + flags.StringVar(&p.name, "name", "", "Required. Name of the redis to create. Range of the name length is [6,63]") flags.StringVar(&redisType, "type", "", "Required. Type of the redis. Accept values:'master-replica','distributed'") flags.IntVar(&p.size, "size-gb", 1, "Optional. Memory size. Default value 1GB(for master-replica redis type) or block-cnt*1GB(for distributed redis type). Unit GB") flags.StringVar(&p.version, "version", "6.0", "Optional. Version of redis. Accept values: '4.0', '5.0', '6.0', '7.0'") flags.StringVar(&p.vpcID, "vpc-id", "", "Optional. VPC ID. This field is required under VPC2.0. See 'ucloud vpc list'") flags.StringVar(&p.subnetID, "subnet-id", "", "Optional. Subnet ID. This field is required under VPC2.0. See 'ucloud subnet list'") - flags.StringVar(&p.password, "password", "", "Optional. Password of redis to create. Range of the password length is [6,63] and the password can only contain letters and numbers") + flags.StringVar(&p.password, "password", "", "Optional. Password of redis to create. Range of the password length is [6,36] and the password can only contain letters and numbers") //distributed optional params flags.IntVar(&p.blockCnt, "block-cnt", 2, "Optional. Block count. Default value 2(for distributed redis type).") flags.IntVar(&p.proxySize, "proxy-size", 2, "Optional. Proxy size. Default value 2(for distributed redis type) Unit Core") - p.region = base.ConfigIns.Region - flags.StringVar(&p.region, "region", base.ConfigIns.Region, "Optional. Override default region for this command invocation, see 'ucloud region'") - p.zone = base.ConfigIns.Zone - flags.StringVar(&p.zone, "zone", base.ConfigIns.Zone, "Optional. Override default availability zone for this command invocation, see 'ucloud region'") - p.projectID = base.ConfigIns.ProjectID - flags.StringVar(&p.projectID, "project-id", base.ConfigIns.ProjectID, "Optional. Override default project-id for this command invocation, see 'ucloud project list'") + bindRegionS(&p.region, flags) + bindZoneS(&p.zone, &p.region, flags) + bindProjectIDS(&p.projectID, flags) flags.StringVar(&p.chargeType, "charge-type", "Month", "Optional. Enumeration value.'Year',pay yearly;'Month',pay monthly; 'Dynamic', pay hourly; 'Trial', free trial(need permission)") flags.IntVar(&p.quantity, "quantity", 1, "Optional. The duration of the instance. N years/months.") flags.StringVar(&p.group, "group", "", "Optional. Business group") @@ -227,11 +233,6 @@ func NewCmdRedisCreate(out io.Writer) *cobra.Command { flags.SetFlagValues("version", "4.0", "5.0", "6.0", "7.0") flags.SetFlagValues("type", "master-replica", "distributed") flags.SetFlagValues("charge-type", "Month", "Dynamic", "Year") - flags.SetFlagValuesFunc("region", getRegionList) - flags.SetFlagValuesFunc("zone", func() []string { - return getZoneList(p.region) - }) - flags.SetFlagValuesFunc("project-id", getProjectList) flags.SetFlagValuesFunc("vpc-id", func() []string { return getAllVPCIdNames(p.projectID, p.region) }) @@ -278,19 +279,28 @@ func createDistributedRedis(out io.Writer, p *redisCreateParams) { req.ProjectId = &p.projectID req.Name = &p.name req.Protocol = sdk.String("redis") - req.ProxySize = &p.proxySize + + // Validate block-cnt if p.blockCnt <= 0 { fmt.Fprintln(out, "block-cnt should be greater than 0") return } - req.BlockCnt = &p.blockCnt - size := p.size - if size < p.blockCnt || size%p.blockCnt != 0 { - size = p.blockCnt + // Validate size is divisible by block-cnt + if p.size%p.blockCnt != 0 { + fmt.Fprintf(out, "size-gb(%d) should be divisible by block-cnt(%d)\n", p.size, p.blockCnt) + return } - req.Size = &size + // Validate proxy-size is a multiple of 2 + if p.proxySize%2 != 0 { + fmt.Fprintf(out, "proxy-size(%d) should be a multiple of 2\n", p.proxySize) + return + } + + req.BlockCnt = &p.blockCnt + req.ProxySize = &p.proxySize + req.Size = &p.size req.Version = &p.version req.VPCId = &p.vpcID req.SubnetId = &p.subnetID @@ -343,23 +353,15 @@ func NewCmdRedisDelete(out io.Writer) *cobra.Command { flags.SortFlags = false flags.StringSliceVar(&idNames, "umem-id", nil, "Required. Resource ID of redis instances to delete") - p.region = base.ConfigIns.Region - flags.StringVar(&p.region, "region", base.ConfigIns.Region, "Optional. Override default region for this command invocation, see 'ucloud region'") - p.zone = base.ConfigIns.Zone - flags.StringVar(&p.zone, "zone", base.ConfigIns.Zone, "Optional. Override default availability zone for this command invocation, see 'ucloud region'") - p.projectID = base.ConfigIns.ProjectID - flags.StringVar(&p.projectID, "project-id", base.ConfigIns.ProjectID, "Optional. Override default project-id for this command invocation, see 'ucloud project list'") + bindRegionS(&p.region, flags) + bindZoneS(&p.zone, &p.region, flags) + bindProjectIDS(&p.projectID, flags) cmd.MarkFlagRequired("umem-id") flags.SetFlagValuesFunc("umem-id", func() []string { return getRedisIDList(p.projectID, p.region) }) - flags.SetFlagValuesFunc("region", getRegionList) - flags.SetFlagValuesFunc("zone", func() []string { - return getZoneList(p.region) - }) - flags.SetFlagValuesFunc("project-id", getProjectList) return cmd } @@ -566,12 +568,9 @@ func NewCmdMemcacheCreate(out io.Writer) *cobra.Command { req.Size = flags.Int("size-gb", 1, "Optional. Memory size of memcache instance. Unit GB. Accpet values:1,2,4,8,16,32") req.VPCId = flags.String("vpc-id", "", "Optional. VPC ID. See 'ucloud vpc list'") req.SubnetId = flags.String("subnet-id", "", "Optional. Subnet ID. See 'ucloud subnet list'") - region = base.ConfigIns.Region - flags.StringVar(®ion, "region", base.ConfigIns.Region, "Optional. Override default region for this command invocation, see 'ucloud region'") - zone = base.ConfigIns.Zone - flags.StringVar(&zone, "zone", base.ConfigIns.Zone, "Optional. Override default availability zone for this command invocation, see 'ucloud region'") - projectID = base.ConfigIns.ProjectID - flags.StringVar(&projectID, "project-id", base.ConfigIns.ProjectID, "Optional. Override default project-id for this command invocation, see 'ucloud project list'") + bindRegionS(®ion, flags) + bindZoneS(&zone, ®ion, flags) + bindProjectIDS(&projectID, flags) req.ChargeType = flags.String("charge-type", "Month", "Optional. Enumeration value.'Year',pay yearly;'Month',pay monthly; 'Dynamic', pay hourly; 'Trial', free trial(need permission)") req.Quantity = flags.Int("quantity", 1, "Optional. The duration of the instance. N years/months.") req.Tag = flags.String("group", "", "Optional. Business group") @@ -589,11 +588,6 @@ func NewCmdMemcacheCreate(out io.Writer) *cobra.Command { flags.SetFlagValuesFunc("subnet-id", func() []string { return getAllSubnetIDNames(*req.VPCId, projectID, region) }) - flags.SetFlagValuesFunc("region", getRegionList) - flags.SetFlagValuesFunc("zone", func() []string { - return getZoneList(region) - }) - flags.SetFlagValuesFunc("project-id", getProjectList) cmd.MarkFlagRequired("name") From 10fe6850082cfc34c3fadb7f0186a4a7f30f0d93 Mon Sep 17 00:00:00 2001 From: "qingping.fang" Date: Wed, 17 Jun 2026 18:49:27 +0800 Subject: [PATCH 4/4] feat(umem): update size default to 2GB and support uregionredis delete - Change size-gb default value from 1GB to 2GB - Simplify size-gb help text - Add support for 'uregionredis' prefix in redis delete command Co-Authored-By: Claude --- cmd/umem.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/umem.go b/cmd/umem.go index a667329574..62f01f2b15 100644 --- a/cmd/umem.go +++ b/cmd/umem.go @@ -213,7 +213,7 @@ func NewCmdRedisCreate(out io.Writer) *cobra.Command { flags.StringVar(&p.name, "name", "", "Required. Name of the redis to create. Range of the name length is [6,63]") flags.StringVar(&redisType, "type", "", "Required. Type of the redis. Accept values:'master-replica','distributed'") - flags.IntVar(&p.size, "size-gb", 1, "Optional. Memory size. Default value 1GB(for master-replica redis type) or block-cnt*1GB(for distributed redis type). Unit GB") + flags.IntVar(&p.size, "size-gb", 2, "Optional. Memory size. Default value 2GB. Unit GB") flags.StringVar(&p.version, "version", "6.0", "Optional. Version of redis. Accept values: '4.0', '5.0', '6.0', '7.0'") flags.StringVar(&p.vpcID, "vpc-id", "", "Optional. VPC ID. This field is required under VPC2.0. See 'ucloud vpc list'") flags.StringVar(&p.subnetID, "subnet-id", "", "Optional. Subnet ID. This field is required under VPC2.0. See 'ucloud subnet list'") @@ -338,7 +338,7 @@ func NewCmdRedisDelete(out io.Writer) *cobra.Command { Run: func(c *cobra.Command, args []string) { for _, idname := range idNames { id := base.PickResourceID(idname) - if strings.HasPrefix(id, "uredis") || strings.HasPrefix(id, "uhredis") { + if strings.HasPrefix(id, "uredis") || strings.HasPrefix(id, "uhredis") || strings.HasPrefix(id, "uregionredis") { deleteMasterReplicaRedis(out, &p, id) } else if strings.HasPrefix(id, "udredis") { deleteDistributedRedis(out, &p, id)