Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/products.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions products/nlb/internal/nlb/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package nlb

import (
"github.com/spf13/cobra"

"github.com/ucloud/ucloud-cli/pkg/cli"
)

// NewCommand assembles the `nlb` command tree. This aggregator only constructs
// the top-level command and AddCommand's one constructor per verb / sub-group
// (§2.2 file-layout convention): no business logic lives here.
func NewCommand(ctx *cli.Context) *cobra.Command {
cmd := &cobra.Command{
Use: "nlb",
Short: "List and manipulate NLB (Network Load Balancer) instances",
Long: "List and manipulate NLB (Network Load Balancer) instances",
}

cmd.AddCommand(newList(ctx))
cmd.AddCommand(newDescribe(ctx))
cmd.AddCommand(newCreate(ctx))
cmd.AddCommand(newUpdate(ctx))
cmd.AddCommand(newDelete(ctx))
cmd.AddCommand(newListener(ctx))
cmd.AddCommand(newTarget(ctx))

return cmd
}
157 changes: 157 additions & 0 deletions products/nlb/internal/nlb/completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package nlb

import (
"fmt"

nlbsdk "github.com/ucloud/ucloud-sdk-go/services/nlb"
"github.com/ucloud/ucloud-sdk-go/services/vpc"
sdk "github.com/ucloud/ucloud-sdk-go/ucloud"

"github.com/ucloud/ucloud-cli/pkg/cli"
)

// productName is the single source of truth for the product's command name and
// its resource-id flag (`--nlb-id`).
const productName = "nlb"

// resourceIDFlag is the NLB instance resource-id flag, named after the product.
const resourceIDFlag = productName + "-id" // "nlb-id"

// getAllNLB returns every NLB instance in the active region/project, paging
// through the DescribeNetworkLoadBalancers result set.
func getAllNLB(ctx *cli.Context, project, region string) ([]nlbsdk.NetworkLoadBalancer, error) {
client := cli.NewServiceClient(ctx, nlbsdk.NewClient)
req := client.NewDescribeNetworkLoadBalancersRequest()
req.ProjectId = sdk.String(cli.PickResourceID(project))
req.Region = sdk.String(region)

list := []nlbsdk.NetworkLoadBalancer{}
for offset, limit := 0, 100; ; offset += limit {
req.Offset = sdk.Int(offset)
req.Limit = sdk.Int(limit)
resp, err := client.DescribeNetworkLoadBalancers(req)
if err != nil {
return nil, err
}
list = append(list, resp.NLBs...)
if offset+limit >= resp.TotalCount {
break
}
}
return list, nil
}

// getAllNLBIDNames returns "nlbId/name" completion candidates for --nlb-id.
func getAllNLBIDNames(ctx *cli.Context, project, region string) []string {
list, err := getAllNLB(ctx, project, region)
if err != nil {
return nil
}
idNames := make([]string, 0, len(list))
for _, n := range list {
idNames = append(idNames, fmt.Sprintf("%s/%s", n.NLBId, n.Name))
}
return idNames
}

// getAllListeners returns the listeners of a given NLB instance.
func getAllListeners(ctx *cli.Context, nlbID, project, region string) ([]nlbsdk.Listener, error) {
if nlbID == "" {
return nil, fmt.Errorf("nlb-id can't be empty")
}
client := cli.NewServiceClient(ctx, nlbsdk.NewClient)
req := client.NewDescribeNLBListenersRequest()
req.ProjectId = sdk.String(cli.PickResourceID(project))
req.Region = sdk.String(region)
req.NLBId = sdk.String(cli.PickResourceID(nlbID))
resp, err := client.DescribeNLBListeners(req)
if err != nil {
return nil, err
}
return resp.Listeners, nil
}

// getAllListenerIDNames returns "listenerId/name" completion candidates.
func getAllListenerIDNames(ctx *cli.Context, nlbID, project, region string) []string {
listeners, err := getAllListeners(ctx, nlbID, project, region)
if err != nil {
return nil
}
idNames := make([]string, 0, len(listeners))
for _, l := range listeners {
idNames = append(idNames, fmt.Sprintf("%s/%s", l.ListenerId, l.Name))
}
return idNames
}

// getAllTargetIDNames returns the target ids attached to a listener, in
// "targetId/resource" form for --target-id completion.
func getAllTargetIDNames(ctx *cli.Context, nlbID, listenerID, project, region string) []string {
listeners, err := getAllListeners(ctx, nlbID, project, region)
if err != nil {
return nil
}
wantListener := cli.PickResourceID(listenerID)
idNames := []string{}
for _, l := range listeners {
if wantListener != "" && l.ListenerId != wantListener {
continue
}
for _, t := range l.Targets {
label := t.ResourceId
if label == "" {
label = t.ResourceIP
}
idNames = append(idNames, fmt.Sprintf("%s/%s", t.Id, label))
}
}
return idNames
}

// getAllVPCIDNames returns "vpcId/name" candidates via the VPC SDK service
// package. Cross-product completion uses the peer SDK directly, never imports
// products/vpc (§8, check-product rule 1).
func getAllVPCIDNames(ctx *cli.Context, project, region string) []string {
client := cli.NewServiceClient(ctx, vpc.NewClient)
req := client.NewDescribeVPCRequest()
req.ProjectId = sdk.String(cli.PickResourceID(project))
req.Region = sdk.String(region)
resp, err := client.DescribeVPC(req)
if err != nil {
return nil
}
idNames := make([]string, 0, len(resp.DataSet))
for _, v := range resp.DataSet {
idNames = append(idNames, fmt.Sprintf("%s/%s", v.VPCId, v.Name))
}
return idNames
}

// getAllSubnetIDNames returns "subnetId/name" candidates, optionally scoped to
// a VPC, via the VPC SDK service package.
func getAllSubnetIDNames(ctx *cli.Context, vpcID, project, region string) []string {
client := cli.NewServiceClient(ctx, vpc.NewClient)
req := client.NewDescribeSubnetRequest()
req.ProjectId = sdk.String(cli.PickResourceID(project))
req.Region = sdk.String(region)
if vpcID != "" {
req.VPCId = sdk.String(cli.PickResourceID(vpcID))
}
resp, err := client.DescribeSubnet(req)
if err != nil {
return nil
}
idNames := make([]string, 0, len(resp.DataSet))
for _, s := range resp.DataSet {
idNames = append(idNames, fmt.Sprintf("%s/%s", s.SubnetId, s.SubnetName))
}
return idNames
}

// derefStr safely dereferences a *string bound by a flag.
func derefStr(p *string) string {
if p == nil {
return ""
}
return *p
}
73 changes: 73 additions & 0 deletions products/nlb/internal/nlb/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package nlb

import (
"fmt"

"github.com/spf13/cobra"

nlbsdk "github.com/ucloud/ucloud-sdk-go/services/nlb"
sdk "github.com/ucloud/ucloud-sdk-go/ucloud"

"github.com/ucloud/ucloud-cli/pkg/cli"
"github.com/ucloud/ucloud-cli/pkg/command"
)

// newCreate implements `nlb create`.
func newCreate(ctx *cli.Context) *cobra.Command {
client := cli.NewServiceClient(ctx, nlbsdk.NewClient)
req := client.NewCreateNetworkLoadBalancerRequest()

var couponID string

cmd := &cobra.Command{
Use: "create",
Short: "Create an NLB instance",
Long: "Create an NLB (Network Load Balancer) instance in the specified VPC and subnet.",
Run: func(c *cobra.Command, args []string) {
req.ProjectId = sdk.String(ctx.PickResourceID(*req.ProjectId))
req.VPCId = sdk.String(ctx.PickResourceID(*req.VPCId))
req.SubnetId = sdk.String(ctx.PickResourceID(*req.SubnetId))
if c.Flags().Changed("coupon-id") {
req.CouponId = &couponID
}

resp, err := client.CreateNetworkLoadBalancer(req)
if err != nil {
ctx.HandleError(err)
return
}
fmt.Fprintf(ctx.ProgressWriter(), "nlb[%s] created\n", resp.NLBId)
ctx.EmitResult(cli.OpResultRow{ResourceID: resp.NLBId, Action: "create", Status: "Created"})
},
}

flags := cmd.Flags()
flags.SortFlags = false

ctx.BindRegion(cmd, req)
ctx.BindProjectID(cmd, req)

req.VPCId = flags.String("vpc-id", "", "Required. Resource ID of the VPC the NLB belongs to. See 'ucloud vpc list'.")
req.SubnetId = flags.String("subnet-id", "", "Required. Resource ID of the subnet the NLB belongs to. See 'ucloud subnet list'.")
req.Name = flags.String("name", "", "Optional. NLB instance name, 1-255 chars.")
req.IPVersion = flags.String("ip-version", "IPv4", "Optional. IP protocol version: IPv4/IPv6/DualStack.")
req.ChargeType = flags.String("charge-type", "Dynamic", "Optional. Charge type: Dynamic (by hour), Month, Year.")
req.Quantity = flags.Int("quantity", 1, "Optional. Purchase duration. For Month with value 0 means until end of month.")
req.Tag = flags.String("group", "Default", "Optional. Business group.")
req.Remark = flags.String("remark", "", "Optional. Remark of the NLB instance.")
flags.StringVar(&couponID, "coupon-id", "", "Optional. Coupon ID.")

command.SetFlagValues(cmd, "ip-version", "IPv4", "IPv6", "DualStack")
command.SetFlagValues(cmd, "charge-type", "Dynamic", "Month", "Year")
command.SetCompletion(cmd, "vpc-id", func() []string {
return getAllVPCIDNames(ctx, derefStr(req.ProjectId), derefStr(req.Region))
})
command.SetCompletion(cmd, "subnet-id", func() []string {
return getAllSubnetIDNames(ctx, derefStr(req.VPCId), derefStr(req.ProjectId), derefStr(req.Region))
})

cmd.MarkFlagRequired("vpc-id")
cmd.MarkFlagRequired("subnet-id")

return cmd
}
68 changes: 68 additions & 0 deletions products/nlb/internal/nlb/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package nlb

import (
"fmt"

"github.com/spf13/cobra"

nlbsdk "github.com/ucloud/ucloud-sdk-go/services/nlb"
sdk "github.com/ucloud/ucloud-sdk-go/ucloud"

"github.com/ucloud/ucloud-cli/pkg/cli"
"github.com/ucloud/ucloud-cli/pkg/command"
)

// newDelete implements `nlb delete`.
func newDelete(ctx *cli.Context) *cobra.Command {
client := cli.NewServiceClient(ctx, nlbsdk.NewClient)
req := client.NewDeleteNetworkLoadBalancerRequest()

var idNames []string
var yes bool

cmd := &cobra.Command{
Use: "delete",
Short: "Delete NLB instances by resource ID",
Long: "Delete one or more NLB instances by resource ID.",
Run: func(c *cobra.Command, args []string) {
ok, err := ctx.Confirm(yes, "Are you sure you want to delete the NLB instance(s)?")
if err != nil {
ctx.HandleError(err)
return
}
if !ok {
return
}
req.ProjectId = sdk.String(ctx.PickResourceID(*req.ProjectId))
results := []cli.OpResultRow{}
for _, idName := range idNames {
id := ctx.PickResourceID(idName)
req.NLBId = sdk.String(id)
if _, err := client.DeleteNetworkLoadBalancer(req); err != nil {
ctx.HandleError(err)
continue
}
fmt.Fprintf(ctx.ProgressWriter(), "nlb[%s] deleted\n", id)
results = append(results, cli.OpResultRow{ResourceID: id, Action: "delete", Status: "Deleted"})
}
ctx.EmitResult(results...)
},
}

flags := cmd.Flags()
flags.SortFlags = false

ctx.BindRegion(cmd, req)
ctx.BindProjectID(cmd, req)

flags.StringSliceVar(&idNames, resourceIDFlag, nil, "Required. Resource ID(s) of the NLB instances to delete.")
flags.BoolVarP(&yes, "yes", "y", false, "Optional. Skip the confirmation prompt.")
req.ReleaseEIP = flags.Bool("release-eip", false, "Optional. Release the EIP bound to the NLB when deleting.")

cmd.MarkFlagRequired(resourceIDFlag)
command.SetCompletion(cmd, resourceIDFlag, func() []string {
return getAllNLBIDNames(ctx, derefStr(req.ProjectId), derefStr(req.Region))
})

return cmd
}
Loading
Loading