From 5b7604721ea5eca851c0e58ab95478f13ab4fede Mon Sep 17 00:00:00 2001 From: "pearlin.pan" Date: Thu, 9 Jul 2026 15:20:04 +0800 Subject: [PATCH 1/4] add product:ufs upfs usnap --- cmd/products.gen.go | 6 ++ go.mod | 2 +- go.sum | 4 +- products/udisk/product.yaml | 2 +- products/ufs/internal/ufs/cmd.go | 20 ++++++ products/ufs/internal/ufs/create.go | 57 +++++++++++++++ products/ufs/internal/ufs/delete.go | 60 ++++++++++++++++ products/ufs/internal/ufs/describe.go | 82 ++++++++++++++++++++++ products/ufs/internal/ufs/rows.go | 16 +++++ products/ufs/internal/ufs/status.go | 8 +++ products/ufs/product.go | 21 ++++++ products/ufs/product.yaml | 7 ++ products/ufs/testdata/cmdtree.golden | 27 +++++++ products/ufs/testdata/completion.golden | 4 ++ products/upfs/internal/upfs/cmd.go | 20 ++++++ products/upfs/internal/upfs/create.go | 55 +++++++++++++++ products/upfs/internal/upfs/delete.go | 60 ++++++++++++++++ products/upfs/internal/upfs/describe.go | 81 +++++++++++++++++++++ products/upfs/internal/upfs/rows.go | 15 ++++ products/upfs/internal/upfs/status.go | 8 +++ products/upfs/product.go | 21 ++++++ products/upfs/product.yaml | 7 ++ products/upfs/testdata/cmdtree.golden | 26 +++++++ products/upfs/testdata/completion.golden | 3 + products/usnap/internal/usnap/cmd.go | 20 ++++++ products/usnap/internal/usnap/create.go | 60 ++++++++++++++++ products/usnap/internal/usnap/delete.go | 55 +++++++++++++++ products/usnap/internal/usnap/describe.go | 85 +++++++++++++++++++++++ products/usnap/internal/usnap/rows.go | 17 +++++ products/usnap/internal/usnap/status.go | 8 +++ products/usnap/product.go | 21 ++++++ products/usnap/product.yaml | 7 ++ products/usnap/testdata/cmdtree.golden | 29 ++++++++ products/usnap/testdata/completion.golden | 2 + 34 files changed, 912 insertions(+), 4 deletions(-) create mode 100644 products/ufs/internal/ufs/cmd.go create mode 100644 products/ufs/internal/ufs/create.go create mode 100644 products/ufs/internal/ufs/delete.go create mode 100644 products/ufs/internal/ufs/describe.go create mode 100644 products/ufs/internal/ufs/rows.go create mode 100644 products/ufs/internal/ufs/status.go create mode 100644 products/ufs/product.go create mode 100644 products/ufs/product.yaml create mode 100644 products/ufs/testdata/cmdtree.golden create mode 100644 products/ufs/testdata/completion.golden create mode 100644 products/upfs/internal/upfs/cmd.go create mode 100644 products/upfs/internal/upfs/create.go create mode 100644 products/upfs/internal/upfs/delete.go create mode 100644 products/upfs/internal/upfs/describe.go create mode 100644 products/upfs/internal/upfs/rows.go create mode 100644 products/upfs/internal/upfs/status.go create mode 100644 products/upfs/product.go create mode 100644 products/upfs/product.yaml create mode 100644 products/upfs/testdata/cmdtree.golden create mode 100644 products/upfs/testdata/completion.golden create mode 100644 products/usnap/internal/usnap/cmd.go create mode 100644 products/usnap/internal/usnap/create.go create mode 100644 products/usnap/internal/usnap/delete.go create mode 100644 products/usnap/internal/usnap/describe.go create mode 100644 products/usnap/internal/usnap/rows.go create mode 100644 products/usnap/internal/usnap/status.go create mode 100644 products/usnap/product.go create mode 100644 products/usnap/product.yaml create mode 100644 products/usnap/testdata/cmdtree.golden create mode 100644 products/usnap/testdata/completion.golden diff --git a/cmd/products.gen.go b/cmd/products.gen.go index 40a32aa6a5..c7fa2285ab 100644 --- a/cmd/products.gen.go +++ b/cmd/products.gen.go @@ -5,9 +5,12 @@ import ( "github.com/ucloud/ucloud-cli/pkg/cli" "github.com/ucloud/ucloud-cli/products/udb" "github.com/ucloud/ucloud-cli/products/udisk" + "github.com/ucloud/ucloud-cli/products/ufs" "github.com/ucloud/ucloud-cli/products/uhost" "github.com/ucloud/ucloud-cli/products/unet" + "github.com/ucloud/ucloud-cli/products/upfs" "github.com/ucloud/ucloud-cli/products/uphost" + "github.com/ucloud/ucloud-cli/products/usnap" ) // registeredProducts returns the platform-registered products. @@ -15,8 +18,11 @@ func registeredProducts() []cli.Product { return []cli.Product{ udb.New(), udisk.New(), + ufs.New(), uhost.New(), unet.New(), + upfs.New(), uphost.New(), + usnap.New(), } } diff --git a/go.mod b/go.mod index becaa0b830..fdf0db06af 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/sirupsen/logrus v1.3.0 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 - github.com/ucloud/ucloud-sdk-go v0.22.25 + github.com/ucloud/ucloud-sdk-go v0.22.87 golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 gopkg.in/yaml.v2 v2.2.2 ) diff --git a/go.sum b/go.sum index 1dfa7c9e96..0a0eb8db2d 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A 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.22.25 h1:ceKeH7WFnpUt9nJSubn+mnxS1iKGrk/Q+HLwa0iYwmQ= -github.com/ucloud/ucloud-sdk-go v0.22.25/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw= +github.com/ucloud/ucloud-sdk-go v0.22.87 h1:IAQBH1bl27+nBlBFo/hx5mMuDOMQHFks7cpOTfgxK3U= +github.com/ucloud/ucloud-sdk-go v0.22.87/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/products/udisk/product.yaml b/products/udisk/product.yaml index b77f48efa3..20712b0a23 100644 --- a/products/udisk/product.yaml +++ b/products/udisk/product.yaml @@ -1,7 +1,7 @@ # products/udisk/product.yaml — udisk 产品元数据(归属真源,owner 自治维护) name: udisk owners: - - Episkey-G + - pearlin-pan commands: - udisk enabled: true diff --git a/products/ufs/internal/ufs/cmd.go b/products/ufs/internal/ufs/cmd.go new file mode 100644 index 0000000000..8fc245f82c --- /dev/null +++ b/products/ufs/internal/ufs/cmd.go @@ -0,0 +1,20 @@ +package ufs + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +// NewCommand builds the `ufs` root command and mounts the subcommands. +func NewCommand(ctx *cli.Context) *cobra.Command { + cmd := &cobra.Command{ + Use: "ufs", + Short: "Manage UFS (UCloud File Storage) volumes", + Long: "Manage UFS (UCloud File Storage) volumes", + } + cmd.AddCommand(newCreate(ctx)) + cmd.AddCommand(newDescribe(ctx)) + cmd.AddCommand(newDelete(ctx)) + return cmd +} diff --git a/products/ufs/internal/ufs/create.go b/products/ufs/internal/ufs/create.go new file mode 100644 index 0000000000..8bf112a3e2 --- /dev/null +++ b/products/ufs/internal/ufs/create.go @@ -0,0 +1,57 @@ +package ufs + +import ( + "fmt" + + "github.com/spf13/cobra" + + ufssdk "github.com/ucloud/ucloud-sdk-go/services/ufs" + + "github.com/ucloud/ucloud-cli/pkg/cli" + "github.com/ucloud/ucloud-cli/pkg/command" +) + +// newCreate ucloud ufs create +func newCreate(ctx *cli.Context) *cobra.Command { + client := cli.NewServiceClient(ctx, ufssdk.NewClient) + req := client.NewCreateUFSVolumeRequest() + cmd := &cobra.Command{ + Use: "create", + Short: "Create a UFS volume", + Long: "Create a UFS volume", + Run: func(cmd *cobra.Command, args []string) { + w := ctx.ProgressWriter() + resp, err := client.CreateUFSVolume(req) + if err != nil { + ctx.HandleError(err) + return + } + + text := fmt.Sprintf("ufs:%v created", resp.VolumeId) + fmt.Fprintln(w, text) + ctx.EmitResult(cli.OpResultRow{ResourceID: resp.VolumeId, Action: "create", Status: "Created"}) + }, + } + flags := cmd.Flags() + flags.SortFlags = false + req.VolumeName = flags.String("name", "", "Required. Name of the UFS volume to create") + req.Size = flags.Int("size-gb", 100, "Required. Size of the UFS volume. Unit: GB") + req.StorageType = flags.String("storage-type", "Basic", "Optional. Storage type: 'Basic' (capacity) or 'Advanced' (performance)") + req.ProtocolType = flags.String("protocol-type", "NFS", "Optional. Protocol type: 'NFS' or 'SMB'") + req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") + req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") + req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") + req.ChargeType = flags.String("charge-type", "Dynamic", "Optional. 'Year', pay yearly; 'Month', pay monthly; 'Dynamic', pay hourly") + req.Quantity = flags.Int("quantity", 1, "Optional. The duration of the instance. N years/months") + req.Tag = flags.String("group", "Default", "Optional. Business group") + req.Remark = flags.String("remark", "", "Optional. Remark") + + command.SetFlagValues(cmd, "charge-type", "Month", "Year", "Dynamic", "Trial") + command.SetFlagValues(cmd, "storage-type", "Basic", "Advanced") + command.SetFlagValues(cmd, "protocol-type", "NFS", "SMB") + + cmd.MarkFlagRequired("name") + cmd.MarkFlagRequired("size-gb") + + return cmd +} diff --git a/products/ufs/internal/ufs/delete.go b/products/ufs/internal/ufs/delete.go new file mode 100644 index 0000000000..ec68bc12ad --- /dev/null +++ b/products/ufs/internal/ufs/delete.go @@ -0,0 +1,60 @@ +package ufs + +import ( + "fmt" + + "github.com/spf13/cobra" + + ufssdk "github.com/ucloud/ucloud-sdk-go/services/ufs" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +// newDelete ucloud ufs delete +func newDelete(ctx *cli.Context) *cobra.Command { + var yes *bool + var volumeIDs *[]string + client := cli.NewServiceClient(ctx, ufssdk.NewClient) + req := client.NewRemoveUFSVolumeRequest() + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete UFS volume(s)", + Long: "Delete UFS volume(s)", + Run: func(cmd *cobra.Command, args []string) { + ok, err := ctx.Confirm(*yes, "Are you sure to delete UFS volume(s)?") + if err != nil { + ctx.HandleError(err) + return + } + if !ok { + return + } + w := ctx.ProgressWriter() + results := []cli.OpResultRow{} + for _, id := range *volumeIDs { + id := ctx.PickResourceID(id) + req.VolumeId = &id + _, err := client.RemoveUFSVolume(req) + if err != nil { + ctx.HandleError(err) + continue + } else { + fmt.Fprintf(w, "ufs[%s] deleted\n", *req.VolumeId) + results = append(results, cli.OpResultRow{ResourceID: *req.VolumeId, Action: "delete", Status: "Deleted"}) + } + } + ctx.EmitResult(results...) + }, + } + flags := cmd.Flags() + flags.SortFlags = false + volumeIDs = flags.StringSlice("volume-id", nil, "Required. The Resource ID of UFS volumes to delete") + req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") + req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") + req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") + yes = flags.BoolP("yes", "y", false, "Optional. Do not prompt for confirmation.") + + cmd.MarkFlagRequired("volume-id") + + return cmd +} diff --git a/products/ufs/internal/ufs/describe.go b/products/ufs/internal/ufs/describe.go new file mode 100644 index 0000000000..9fec2c2f5a --- /dev/null +++ b/products/ufs/internal/ufs/describe.go @@ -0,0 +1,82 @@ +package ufs + +import ( + "fmt" + + "github.com/spf13/cobra" + + ufssdk "github.com/ucloud/ucloud-sdk-go/services/ufs" + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + + "github.com/ucloud/ucloud-cli/internal/common" + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +// newDescribe ucloud ufs describe +func newDescribe(ctx *cli.Context) *cobra.Command { + client := cli.NewServiceClient(ctx, ufssdk.NewClient) + req := client.NewDescribeUFSVolume2Request() + cmd := &cobra.Command{ + Use: "describe", + Short: "Describe UFS volume(s)", + Long: "Describe UFS volume(s)", + Run: func(cmd *cobra.Command, args []string) { + resp, err := client.DescribeUFSVolume2(req) + if err != nil { + ctx.HandleError(err) + return + } + list := []VolumeRow{} + for _, vol := range resp.DataSet { + row := VolumeRow{ + ResourceID: vol.VolumeId, + Name: vol.VolumeName, + Group: vol.Tag, + Size: fmt.Sprintf("%dGB", vol.Size), + UsedSize: fmt.Sprintf("%dGB", vol.UsedSize), + ProtocolType: vol.ProtocolType, + StorageType: vol.StorageType, + MountPoints: fmt.Sprintf("%d/%d", vol.TotalMountPointNum, vol.MaxMountPointNum), + State: vol.IsExpired, + CreationTime: common.FormatDate(vol.CreateTime), + Expiration: common.FormatDate(vol.ExpiredTime), + } + list = append(list, row) + } + ctx.PrintList(list) + }, + } + flags := cmd.Flags() + flags.SortFlags = false + req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") + req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") + req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") + req.VolumeId = flags.String("volume-id", "", "Optional. Resource ID of the UFS volume") + req.Limit = flags.Int("limit", 50, "Optional. Limit") + req.Offset = flags.Int("offset", 0, "Optional. Offset") + + return cmd +} + +// describeUfsByID returns the poller's describe func, closing over ctx so it +// can build an authed ufs client. +func describeUfsByID(ctx *cli.Context) func(volumeID string, commonBase *request.CommonBase) (interface{}, error) { + return func(volumeID string, commonBase *request.CommonBase) (interface{}, error) { + client := cli.NewServiceClient(ctx, ufssdk.NewClient) + req := client.NewDescribeUFSVolume2Request() + if commonBase != nil { + req.CommonBase = *commonBase + } + req.VolumeId = &volumeID + limit := 50 + req.Limit = &limit + resp, err := client.DescribeUFSVolume2(req) + if err != nil { + return nil, err + } + if len(resp.DataSet) < 1 { + return nil, nil + } + return &resp.DataSet[0], nil + } +} diff --git a/products/ufs/internal/ufs/rows.go b/products/ufs/internal/ufs/rows.go new file mode 100644 index 0000000000..dcf95e3c5b --- /dev/null +++ b/products/ufs/internal/ufs/rows.go @@ -0,0 +1,16 @@ +package ufs + +// VolumeRow represents a single row in the ufs volume list output. +type VolumeRow struct { + ResourceID string + Name string + Group string + Size string + UsedSize string + ProtocolType string + StorageType string + MountPoints string + State string + CreationTime string + Expiration string +} diff --git a/products/ufs/internal/ufs/status.go b/products/ufs/internal/ufs/status.go new file mode 100644 index 0000000000..6d14f6ed02 --- /dev/null +++ b/products/ufs/internal/ufs/status.go @@ -0,0 +1,8 @@ +package ufs + +// UFS-domain state constants. +const ( + VOLUME_CREATING = "Creating" + VOLUME_AVAILABLE = "Available" + VOLUME_FAILED = "Failed" +) diff --git a/products/ufs/product.go b/products/ufs/product.go new file mode 100644 index 0000000000..9d03bbe6c1 --- /dev/null +++ b/products/ufs/product.go @@ -0,0 +1,21 @@ +package ufs + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" + internalufs "github.com/ucloud/ucloud-cli/products/ufs/internal/ufs" +) + +type product struct{} + +// New returns the ufs product (registered via hack/gen-products). +func New() cli.Product { return product{} } + +func (product) Metadata() cli.Metadata { + return cli.Metadata{Name: "ufs", Commands: []string{"ufs"}} +} + +func (product) NewCommand(ctx *cli.Context) []*cobra.Command { + return []*cobra.Command{internalufs.NewCommand(ctx)} +} diff --git a/products/ufs/product.yaml b/products/ufs/product.yaml new file mode 100644 index 0000000000..4693081e69 --- /dev/null +++ b/products/ufs/product.yaml @@ -0,0 +1,7 @@ +# products/ufs/product.yaml — ufs 产品元数据(归属真源,owner 自治维护) +name: ufs +owners: + - Episkey-G +commands: + - ufs +enabled: true diff --git a/products/ufs/testdata/cmdtree.golden b/products/ufs/testdata/cmdtree.golden new file mode 100644 index 0000000000..bbed2886aa --- /dev/null +++ b/products/ufs/testdata/cmdtree.golden @@ -0,0 +1,27 @@ +ucloud ufs use=ufs short=Manage UFS (UCloud File Storage) volumes +ucloud ufs create use=create short=Create a UFS volume + flag=async short= default=false required= + flag=charge-type short= default=Dynamic required= + flag=group short= default=Default required= + flag=name short= default= required=true + flag=project-id short= default= required= + flag=protocol-type short= default=NFS required= + flag=quantity short= default=1 required= + flag=region short= default= required= + flag=remark short= default= required= + flag=size-gb short= default=100 required=true + flag=storage-type short= default=SATA required= + flag=zone short= default= required= +ucloud ufs delete use=delete short=Delete UFS volume(s) + flag=project-id short= default= required= + flag=region short= default= required= + flag=volume-id short= default=[] required=true + flag=yes short=y default=false required= + flag=zone short= default= required= +ucloud ufs describe use=describe short=Describe UFS volume(s) + flag=limit short= default=50 required= + flag=offset short= default=0 required= + flag=project-id short= default= required= + flag=region short= default= required= + flag=volume-id short= default= required= + flag=zone short= default= required= diff --git a/products/ufs/testdata/completion.golden b/products/ufs/testdata/completion.golden new file mode 100644 index 0000000000..9c19754e79 --- /dev/null +++ b/products/ufs/testdata/completion.golden @@ -0,0 +1,4 @@ +ucloud ufs create charge-type static Dynamic,Month,Trial,Year +ucloud ufs create protocol-type static NFS,SMB +ucloud ufs create storage-type static Advanced,Basic +ucloud ufs delete volume-id dynamic diff --git a/products/upfs/internal/upfs/cmd.go b/products/upfs/internal/upfs/cmd.go new file mode 100644 index 0000000000..cb4c800aee --- /dev/null +++ b/products/upfs/internal/upfs/cmd.go @@ -0,0 +1,20 @@ +package upfs + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +// NewCommand builds the `upfs` root command and mounts the subcommands. +func NewCommand(ctx *cli.Context) *cobra.Command { + cmd := &cobra.Command{ + Use: "upfs", + Short: "Manage UPFS (UCloud Parallel File Storage) volumes", + Long: "Manage UPFS (UCloud Parallel File Storage) volumes", + } + cmd.AddCommand(newCreate(ctx)) + cmd.AddCommand(newDescribe(ctx)) + cmd.AddCommand(newDelete(ctx)) + return cmd +} diff --git a/products/upfs/internal/upfs/create.go b/products/upfs/internal/upfs/create.go new file mode 100644 index 0000000000..c700cacebe --- /dev/null +++ b/products/upfs/internal/upfs/create.go @@ -0,0 +1,55 @@ +package upfs + +import ( + "fmt" + + "github.com/spf13/cobra" + + upfssdk "github.com/ucloud/ucloud-sdk-go/services/upfs" + + "github.com/ucloud/ucloud-cli/pkg/cli" + "github.com/ucloud/ucloud-cli/pkg/command" +) + +// newCreate ucloud upfs create +func newCreate(ctx *cli.Context) *cobra.Command { + client := cli.NewServiceClient(ctx, upfssdk.NewClient) + req := client.NewCreateUPFSVolumeRequest() + cmd := &cobra.Command{ + Use: "create", + Short: "Create a UPFS volume", + Long: "Create a UPFS volume", + Run: func(cmd *cobra.Command, args []string) { + w := ctx.ProgressWriter() + resp, err := client.CreateUPFSVolume(req) + if err != nil { + ctx.HandleError(err) + return + } + + text := fmt.Sprintf("upfs:%v created", resp.VolumeId) + fmt.Fprintln(w, text) + ctx.EmitResult(cli.OpResultRow{ResourceID: resp.VolumeId, Action: "create", Status: "Created"}) + }, + } + flags := cmd.Flags() + flags.SortFlags = false + req.VolumeName = flags.String("name", "", "Required. Name of the UPFS volume to create") + req.Size = flags.Int("size-gb", 500, "Required. Size of the UPFS volume. Unit: GB, must be a multiple of 100, minimum 500") + req.ProtocolType = flags.String("protocol-type", "POSIX", "Optional. Protocol type, currently only supports POSIX") + req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") + req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") + req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") + req.ChargeType = flags.String("charge-type", "Dynamic", "Optional. 'Year', pay yearly; 'Month', pay monthly; 'Dynamic', pay hourly") + req.Quantity = flags.Int("quantity", 1, "Optional. The duration of the instance. N years/months") + req.Tag = flags.String("group", "Default", "Optional. Business group") + req.Remark = flags.String("remark", "", "Optional. Remark") + + command.SetFlagValues(cmd, "charge-type", "Month", "Year", "Dynamic", "Trial") + command.SetFlagValues(cmd, "protocol-type", "POSIX") + + cmd.MarkFlagRequired("name") + cmd.MarkFlagRequired("size-gb") + + return cmd +} diff --git a/products/upfs/internal/upfs/delete.go b/products/upfs/internal/upfs/delete.go new file mode 100644 index 0000000000..3c14599c21 --- /dev/null +++ b/products/upfs/internal/upfs/delete.go @@ -0,0 +1,60 @@ +package upfs + +import ( + "fmt" + + "github.com/spf13/cobra" + + upfssdk "github.com/ucloud/ucloud-sdk-go/services/upfs" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +// newDelete ucloud upfs delete +func newDelete(ctx *cli.Context) *cobra.Command { + var yes *bool + var volumeIDs *[]string + client := cli.NewServiceClient(ctx, upfssdk.NewClient) + req := client.NewRemoveUPFSVolumeRequest() + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete UPFS volume(s)", + Long: "Delete UPFS volume(s)", + Run: func(cmd *cobra.Command, args []string) { + ok, err := ctx.Confirm(*yes, "Are you sure to delete UPFS volume(s)?") + if err != nil { + ctx.HandleError(err) + return + } + if !ok { + return + } + w := ctx.ProgressWriter() + results := []cli.OpResultRow{} + for _, id := range *volumeIDs { + id := ctx.PickResourceID(id) + req.VolumeId = &id + _, err := client.RemoveUPFSVolume(req) + if err != nil { + ctx.HandleError(err) + continue + } else { + fmt.Fprintf(w, "upfs[%s] deleted\n", *req.VolumeId) + results = append(results, cli.OpResultRow{ResourceID: *req.VolumeId, Action: "delete", Status: "Deleted"}) + } + } + ctx.EmitResult(results...) + }, + } + flags := cmd.Flags() + flags.SortFlags = false + volumeIDs = flags.StringSlice("volume-id", nil, "Required. The Resource ID of UPFS volumes to delete") + req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") + req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") + req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") + yes = flags.BoolP("yes", "y", false, "Optional. Do not prompt for confirmation.") + + cmd.MarkFlagRequired("volume-id") + + return cmd +} diff --git a/products/upfs/internal/upfs/describe.go b/products/upfs/internal/upfs/describe.go new file mode 100644 index 0000000000..5de882b0ee --- /dev/null +++ b/products/upfs/internal/upfs/describe.go @@ -0,0 +1,81 @@ +package upfs + +import ( + "fmt" + + "github.com/spf13/cobra" + + upfssdk "github.com/ucloud/ucloud-sdk-go/services/upfs" + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + + "github.com/ucloud/ucloud-cli/internal/common" + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +// newDescribe ucloud upfs describe +func newDescribe(ctx *cli.Context) *cobra.Command { + client := cli.NewServiceClient(ctx, upfssdk.NewClient) + req := client.NewDescribeUPFSVolumeRequest() + cmd := &cobra.Command{ + Use: "describe", + Short: "Describe UPFS volume(s)", + Long: "Describe UPFS volume(s)", + Run: func(cmd *cobra.Command, args []string) { + resp, err := client.DescribeUPFSVolume(req) + if err != nil { + ctx.HandleError(err) + return + } + list := []VolumeRow{} + for _, vol := range resp.DataSet { + row := VolumeRow{ + ResourceID: vol.VolumeId, + Name: vol.VolumeName, + Group: vol.Tag, + Size: fmt.Sprintf("%dGB", vol.Size), + ProtocolType: vol.ProtocolType, + MountAddress: vol.MountAddress, + ChargeType: vol.ChargeType, + State: vol.IsExpired, + CreationTime: common.FormatDate(vol.CreateTime), + Expiration: common.FormatDate(vol.ExpiredTime), + } + list = append(list, row) + } + ctx.PrintList(list) + }, + } + flags := cmd.Flags() + flags.SortFlags = false + req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") + req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") + req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") + req.VolumeId = flags.String("volume-id", "", "Optional. Resource ID of the UPFS volume") + req.Limit = flags.Int("limit", 50, "Optional. Limit") + req.Offset = flags.Int("offset", 0, "Optional. Offset") + + return cmd +} + +// describeUpfsByID returns the poller's describe func, closing over ctx so it +// can build an authed upfs client. +func describeUpfsByID(ctx *cli.Context) func(volumeID string, commonBase *request.CommonBase) (interface{}, error) { + return func(volumeID string, commonBase *request.CommonBase) (interface{}, error) { + client := cli.NewServiceClient(ctx, upfssdk.NewClient) + req := client.NewDescribeUPFSVolumeRequest() + if commonBase != nil { + req.CommonBase = *commonBase + } + req.VolumeId = &volumeID + limit := 50 + req.Limit = &limit + resp, err := client.DescribeUPFSVolume(req) + if err != nil { + return nil, err + } + if len(resp.DataSet) < 1 { + return nil, nil + } + return &resp.DataSet[0], nil + } +} diff --git a/products/upfs/internal/upfs/rows.go b/products/upfs/internal/upfs/rows.go new file mode 100644 index 0000000000..0895e07812 --- /dev/null +++ b/products/upfs/internal/upfs/rows.go @@ -0,0 +1,15 @@ +package upfs + +// VolumeRow represents a single row in the upfs volume list output. +type VolumeRow struct { + ResourceID string + Name string + Group string + Size string + ProtocolType string + MountAddress string + ChargeType string + State string + CreationTime string + Expiration string +} diff --git a/products/upfs/internal/upfs/status.go b/products/upfs/internal/upfs/status.go new file mode 100644 index 0000000000..d15d9976a3 --- /dev/null +++ b/products/upfs/internal/upfs/status.go @@ -0,0 +1,8 @@ +package upfs + +// UPFS-domain state constants. +const ( + VOLUME_CREATING = "Creating" + VOLUME_AVAILABLE = "Available" + VOLUME_FAILED = "Failed" +) diff --git a/products/upfs/product.go b/products/upfs/product.go new file mode 100644 index 0000000000..36e6ed4dc5 --- /dev/null +++ b/products/upfs/product.go @@ -0,0 +1,21 @@ +package upfs + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" + internalupfs "github.com/ucloud/ucloud-cli/products/upfs/internal/upfs" +) + +type product struct{} + +// New returns the upfs product (registered via hack/gen-products). +func New() cli.Product { return product{} } + +func (product) Metadata() cli.Metadata { + return cli.Metadata{Name: "upfs", Commands: []string{"upfs"}} +} + +func (product) NewCommand(ctx *cli.Context) []*cobra.Command { + return []*cobra.Command{internalupfs.NewCommand(ctx)} +} diff --git a/products/upfs/product.yaml b/products/upfs/product.yaml new file mode 100644 index 0000000000..2b0d01c02f --- /dev/null +++ b/products/upfs/product.yaml @@ -0,0 +1,7 @@ +# products/upfs/product.yaml — upfs 产品元数据(归属真源,owner 自治维护) +name: upfs +owners: + - pearlin-pan +commands: + - upfs +enabled: true diff --git a/products/upfs/testdata/cmdtree.golden b/products/upfs/testdata/cmdtree.golden new file mode 100644 index 0000000000..eb42cd8a91 --- /dev/null +++ b/products/upfs/testdata/cmdtree.golden @@ -0,0 +1,26 @@ +ucloud upfs use=upfs short=Manage UPFS (UCloud Parallel File Storage) volumes +ucloud upfs create use=create short=Create a UPFS volume + flag=async short= default=false required= + flag=charge-type short= default=Dynamic required= + flag=group short= default=Default required= + flag=name short= default= required=true + flag=project-id short= default= required= + flag=protocol-type short= default=POSIX required= + flag=quantity short= default=1 required= + flag=region short= default= required= + flag=remark short= default= required= + flag=size-gb short= default=500 required=true + flag=zone short= default= required= +ucloud upfs delete use=delete short=Delete UPFS volume(s) + flag=project-id short= default= required= + flag=region short= default= required= + flag=volume-id short= default=[] required=true + flag=yes short=y default=false required= + flag=zone short= default= required= +ucloud upfs describe use=describe short=Describe UPFS volume(s) + flag=limit short= default=50 required= + flag=offset short= default=0 required= + flag=project-id short= default= required= + flag=region short= default= required= + flag=volume-id short= default= required= + flag=zone short= default= required= diff --git a/products/upfs/testdata/completion.golden b/products/upfs/testdata/completion.golden new file mode 100644 index 0000000000..c11a9c49a7 --- /dev/null +++ b/products/upfs/testdata/completion.golden @@ -0,0 +1,3 @@ +ucloud upfs create charge-type static Dynamic,Month,Trial,Year +ucloud upfs create protocol-type static POSIX +ucloud upfs delete volume-id dynamic diff --git a/products/usnap/internal/usnap/cmd.go b/products/usnap/internal/usnap/cmd.go new file mode 100644 index 0000000000..4fa43cd8f7 --- /dev/null +++ b/products/usnap/internal/usnap/cmd.go @@ -0,0 +1,20 @@ +package usnap + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +// NewCommand builds the `usnap` root command and mounts the subcommands. +func NewCommand(ctx *cli.Context) *cobra.Command { + cmd := &cobra.Command{ + Use: "usnap", + Short: "Manage USnap (UCloud Disk Snapshot Service)", + Long: "Manage USnap (UCloud Disk Snapshot Service)", + } + cmd.AddCommand(newCreate(ctx)) + cmd.AddCommand(newDescribe(ctx)) + cmd.AddCommand(newDelete(ctx)) + return cmd +} diff --git a/products/usnap/internal/usnap/create.go b/products/usnap/internal/usnap/create.go new file mode 100644 index 0000000000..1e117379a4 --- /dev/null +++ b/products/usnap/internal/usnap/create.go @@ -0,0 +1,60 @@ +package usnap + +import ( + "fmt" + + "github.com/spf13/cobra" + + usnapsdk "github.com/ucloud/ucloud-sdk-go/services/usnap" + + "github.com/ucloud/ucloud-cli/pkg/cli" + "github.com/ucloud/ucloud-cli/pkg/command" +) + +// newCreate ucloud usnap create +func newCreate(ctx *cli.Context) *cobra.Command { + var async *bool + client := cli.NewServiceClient(ctx, usnapsdk.NewClient) + req := client.NewCreateSnapshotServiceRequest() + cmd := &cobra.Command{ + Use: "create", + Short: "Create a USnap snapshot service for a disk", + Long: "Create a USnap snapshot service for a disk", + Run: func(cmd *cobra.Command, args []string) { + w := ctx.ProgressWriter() + resp, err := client.CreateSnapshotService(req) + if err != nil { + ctx.HandleError(err) + return + } + + text := fmt.Sprintf("usnap:%v is creating", resp.SnapshotServiceId) + if *async { + fmt.Fprintln(w, text) + } else { + ctx.PollerTo(w, describeUsnapByID(ctx)).Spoll(resp.SnapshotServiceId, text, []string{SERVICE_AVAILABLE, SERVICE_FAILED}) + } + ctx.EmitResult(cli.OpResultRow{ResourceID: resp.SnapshotServiceId, Action: "create", Status: "Created"}) + }, + } + flags := cmd.Flags() + flags.SortFlags = false + req.VDiskId = flags.String("vdisk-id", "", "Required. Resource ID of the disk to create snapshot service for") + req.BackupMode = flags.String("backup-mode", "", "Optional. Backup mode") + req.Day = flags.Int("backup-day", 0, "Optional. Backup day range") + req.Hour = flags.Int("backup-hour", 0, "Optional. Backup hour") + req.Journal = flags.Int("journal", 0, "Optional. Journal retention count") + req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") + req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") + req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") + req.ChargeType = flags.String("charge-type", "Dynamic", "Optional. 'Year', pay yearly; 'Month', pay monthly; 'Dynamic', pay hourly") + req.Quantity = flags.Int("quantity", 1, "Optional. The duration of the instance. N years/months") + req.Tag = flags.String("group", "Default", "Optional. Business group") + async = flags.Bool("async", false, "Optional. Do not wait for the long-running operation to finish.") + + command.SetFlagValues(cmd, "charge-type", "Month", "Year", "Dynamic", "Trial") + + cmd.MarkFlagRequired("vdisk-id") + + return cmd +} diff --git a/products/usnap/internal/usnap/delete.go b/products/usnap/internal/usnap/delete.go new file mode 100644 index 0000000000..14e43861bb --- /dev/null +++ b/products/usnap/internal/usnap/delete.go @@ -0,0 +1,55 @@ +package usnap + +import ( + "fmt" + + "github.com/spf13/cobra" + + usnapsdk "github.com/ucloud/ucloud-sdk-go/services/usnap" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +// newDelete ucloud usnap delete +func newDelete(ctx *cli.Context) *cobra.Command { + var yes *bool + client := cli.NewServiceClient(ctx, usnapsdk.NewClient) + req := client.NewDeleteSnapshotServiceRequest() + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete USnap snapshot service(s)", + Long: "Delete USnap snapshot service(s)", + Run: func(cmd *cobra.Command, args []string) { + ok, err := ctx.Confirm(*yes, "Are you sure to delete USnap snapshot service(s)?") + if err != nil { + ctx.HandleError(err) + return + } + if !ok { + return + } + w := ctx.ProgressWriter() + results := []cli.OpResultRow{} + + _, err = client.DeleteSnapshotService(req) + if err != nil { + ctx.HandleError(err) + } else { + fmt.Fprintf(w, "usnap[%s] deleted\n", *req.VDiskId) + results = append(results, cli.OpResultRow{ResourceID: *req.VDiskId, Action: "delete", Status: "Deleted"}) + } + ctx.EmitResult(results...) + }, + } + flags := cmd.Flags() + flags.SortFlags = false + req.VDiskId = flags.String("vdisk-id", "", "Required. Resource ID of the disk whose snapshot service to delete") + req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") + req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") + req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") + yes = flags.BoolP("yes", "y", false, "Optional. Do not prompt for confirmation.") + + cmd.MarkFlagRequired("vdisk-id") + + return cmd +} diff --git a/products/usnap/internal/usnap/describe.go b/products/usnap/internal/usnap/describe.go new file mode 100644 index 0000000000..caa1a388ed --- /dev/null +++ b/products/usnap/internal/usnap/describe.go @@ -0,0 +1,85 @@ +package usnap + +import ( + "fmt" + + "github.com/spf13/cobra" + + usnapsdk "github.com/ucloud/ucloud-sdk-go/services/usnap" + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + + "github.com/ucloud/ucloud-cli/internal/common" + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +// newDescribe ucloud usnap describe +func newDescribe(ctx *cli.Context) *cobra.Command { + client := cli.NewServiceClient(ctx, usnapsdk.NewClient) + req := client.NewDescribeSnapshotServiceRequest() + cmd := &cobra.Command{ + Use: "describe", + Short: "Describe USnap snapshot service(s)", + Long: "Describe USnap snapshot service(s)", + Run: func(cmd *cobra.Command, args []string) { + resp, err := client.DescribeSnapshotService(req) + if err != nil { + ctx.HandleError(err) + return + } + list := []SnapshotServiceRow{} + for _, svc := range resp.DataSet { + row := SnapshotServiceRow{ + ResourceID: svc.ServiceId, + VDiskID: svc.VDiskId, + VDiskName: svc.VDiskName, + VDiskSize: fmt.Sprintf("%dGB", svc.VDiskSize), + VDiskType: svc.VDiskType, + Group: svc.Tag, + ChargeType: svc.ChargeType, + AutoRenew: svc.AutoRenew, + Status: svc.Status, + Zone: svc.Zone, + CreationTime: common.FormatDate(svc.CreateTime), + Expiration: common.FormatDate(svc.ExpiredTime), + } + list = append(list, row) + } + ctx.PrintList(list) + }, + } + flags := cmd.Flags() + flags.SortFlags = false + req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") + req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") + req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") + req.SnapshotServiceId = flags.String("service-id", "", "Optional. Resource ID of the snapshot service") + req.VDiskId = flags.String("vdisk-id", "", "Optional. Resource ID of the disk") + req.SnapshotId = flags.String("snapshot-id", "", "Optional. Resource ID of the snapshot") + req.Limit = flags.Int("limit", 50, "Optional. Limit") + req.Offset = flags.Int("offset", 0, "Optional. Offset") + + return cmd +} + +// describeUsnapByID returns the poller's describe func, closing over ctx so it +// can build an authed usnap client. +func describeUsnapByID(ctx *cli.Context) func(serviceID string, commonBase *request.CommonBase) (interface{}, error) { + return func(serviceID string, commonBase *request.CommonBase) (interface{}, error) { + client := cli.NewServiceClient(ctx, usnapsdk.NewClient) + req := client.NewDescribeSnapshotServiceRequest() + if commonBase != nil { + req.CommonBase = *commonBase + } + req.SnapshotServiceId = &serviceID + limit := 50 + req.Limit = &limit + resp, err := client.DescribeSnapshotService(req) + if err != nil { + return nil, err + } + if len(resp.DataSet) < 1 { + return nil, nil + } + return &resp.DataSet[0], nil + } +} diff --git a/products/usnap/internal/usnap/rows.go b/products/usnap/internal/usnap/rows.go new file mode 100644 index 0000000000..e0feb84dda --- /dev/null +++ b/products/usnap/internal/usnap/rows.go @@ -0,0 +1,17 @@ +package usnap + +// SnapshotServiceRow represents a single row in the usnap snapshot service list output. +type SnapshotServiceRow struct { + ResourceID string + VDiskID string + VDiskName string + VDiskSize string + VDiskType string + Group string + ChargeType string + AutoRenew string + Status string + Zone string + CreationTime string + Expiration string +} diff --git a/products/usnap/internal/usnap/status.go b/products/usnap/internal/usnap/status.go new file mode 100644 index 0000000000..700658e9df --- /dev/null +++ b/products/usnap/internal/usnap/status.go @@ -0,0 +1,8 @@ +package usnap + +// USnap-domain state constants. +const ( + SERVICE_CREATING = "Creating" + SERVICE_AVAILABLE = "Available" + SERVICE_FAILED = "Failed" +) diff --git a/products/usnap/product.go b/products/usnap/product.go new file mode 100644 index 0000000000..89f5a30dd3 --- /dev/null +++ b/products/usnap/product.go @@ -0,0 +1,21 @@ +package usnap + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" + internalusnap "github.com/ucloud/ucloud-cli/products/usnap/internal/usnap" +) + +type product struct{} + +// New returns the usnap product (registered via hack/gen-products). +func New() cli.Product { return product{} } + +func (product) Metadata() cli.Metadata { + return cli.Metadata{Name: "usnap", Commands: []string{"usnap"}} +} + +func (product) NewCommand(ctx *cli.Context) []*cobra.Command { + return []*cobra.Command{internalusnap.NewCommand(ctx)} +} diff --git a/products/usnap/product.yaml b/products/usnap/product.yaml new file mode 100644 index 0000000000..112a31cc54 --- /dev/null +++ b/products/usnap/product.yaml @@ -0,0 +1,7 @@ +# products/usnap/product.yaml — usnap 产品元数据(归属真源,owner 自治维护) +name: usnap +owners: + - pearlin-pan +commands: + - usnap +enabled: true diff --git a/products/usnap/testdata/cmdtree.golden b/products/usnap/testdata/cmdtree.golden new file mode 100644 index 0000000000..e426ffa0eb --- /dev/null +++ b/products/usnap/testdata/cmdtree.golden @@ -0,0 +1,29 @@ +ucloud usnap use=usnap short=Manage USnap (UCloud Disk Snapshot Service) +ucloud usnap create use=create short=Create a USnap snapshot service for a disk + flag=async short= default=false required= + flag=backup-day short= default=0 required= + flag=backup-hour short= default=0 required= + flag=backup-mode short= default= required= + flag=charge-type short= default=Dynamic required= + flag=group short= default=Default required= + flag=journal short= default=0 required= + flag=project-id short= default= required= + flag=quantity short= default=1 required= + flag=region short= default= required= + flag=vdisk-id short= default= required=true + flag=zone short= default= required= +ucloud usnap delete use=delete short=Delete USnap snapshot service(s) + flag=project-id short= default= required= + flag=region short= default= required= + flag=vdisk-id short= default= required=true + flag=yes short=y default=false required= + flag=zone short= default= required= +ucloud usnap describe use=describe short=Describe USnap snapshot service(s) + flag=limit short= default=50 required= + flag=offset short= default=0 required= + flag=project-id short= default= required= + flag=region short= default= required= + flag=service-id short= default= required= + flag=snapshot-id short= default= required= + flag=vdisk-id short= default= required= + flag=zone short= default= required= diff --git a/products/usnap/testdata/completion.golden b/products/usnap/testdata/completion.golden new file mode 100644 index 0000000000..75c4c5dc49 --- /dev/null +++ b/products/usnap/testdata/completion.golden @@ -0,0 +1,2 @@ +ucloud usnap create charge-type static Dynamic,Month,Trial,Year +ucloud usnap delete vdisk-id dynamic From 4485c154ccfb6c2643437fa1b02c5cf607a890d5 Mon Sep 17 00:00:00 2001 From: "pearlin.pan" Date: Thu, 9 Jul 2026 17:21:54 +0800 Subject: [PATCH 2/4] fix:merge conflict --- cmd/products.gen.go | 12 ++++++------ go.mod | 2 +- products/udisk/product.yaml | 2 +- products/ufs/internal/ufs/create.go | 7 ++++--- products/ufs/internal/ufs/delete.go | 5 ++--- products/ufs/internal/ufs/describe.go | 7 ++++--- products/ufs/testdata/cmdtree.golden | 3 +-- products/ufs/testdata/completion.golden | 10 +++++++++- products/upfs/internal/upfs/create.go | 7 ++++--- products/upfs/internal/upfs/delete.go | 5 ++--- products/upfs/internal/upfs/describe.go | 7 ++++--- products/upfs/product.yaml | 2 +- products/upfs/testdata/cmdtree.golden | 1 - products/upfs/testdata/completion.golden | 10 +++++++++- products/usnap/internal/usnap/create.go | 7 ++++--- products/usnap/internal/usnap/delete.go | 5 ++--- products/usnap/internal/usnap/describe.go | 7 ++++--- products/usnap/product.yaml | 2 +- products/usnap/testdata/completion.golden | 10 +++++++++- 19 files changed, 68 insertions(+), 43 deletions(-) diff --git a/cmd/products.gen.go b/cmd/products.gen.go index a43e54abae..180d47f326 100644 --- a/cmd/products.gen.go +++ b/cmd/products.gen.go @@ -15,14 +15,14 @@ import ( "github.com/ucloud/ucloud-cli/products/subnet" "github.com/ucloud/ucloud-cli/products/udisk" "github.com/ucloud/ucloud-cli/products/udpn" + "github.com/ucloud/ucloud-cli/products/ufs" "github.com/ucloud/ucloud-cli/products/uhost" "github.com/ucloud/ucloud-cli/products/ulb" "github.com/ucloud/ucloud-cli/products/umodelverse" + "github.com/ucloud/ucloud-cli/products/upfs" "github.com/ucloud/ucloud-cli/products/uphost" - "github.com/ucloud/ucloud-cli/products/vpc" "github.com/ucloud/ucloud-cli/products/usnap" - "github.com/ucloud/ucloud-cli/products/upfs" - "github.com/ucloud/ucloud-cli/products/ufs" + "github.com/ucloud/ucloud-cli/products/vpc" ) // registeredProducts returns the platform-registered products. @@ -40,13 +40,13 @@ func registeredProducts() []cli.Product { subnet.New(), udisk.New(), udpn.New(), + ufs.New(), uhost.New(), ulb.New(), umodelverse.New(), - uphost.New(), - vpc.New(), - ufs.New(), upfs.New(), + uphost.New(), usnap.New(), + vpc.New(), } } diff --git a/go.mod b/go.mod index 2f7314cb07..fdf0db06af 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/sirupsen/logrus v1.3.0 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 - github.com/ucloud/ucloud-sdk-go v0.22.86 + github.com/ucloud/ucloud-sdk-go v0.22.87 golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 gopkg.in/yaml.v2 v2.2.2 ) diff --git a/products/udisk/product.yaml b/products/udisk/product.yaml index 20712b0a23..b77f48efa3 100644 --- a/products/udisk/product.yaml +++ b/products/udisk/product.yaml @@ -1,7 +1,7 @@ # products/udisk/product.yaml — udisk 产品元数据(归属真源,owner 自治维护) name: udisk owners: - - pearlin-pan + - Episkey-G commands: - udisk enabled: true diff --git a/products/ufs/internal/ufs/create.go b/products/ufs/internal/ufs/create.go index 8bf112a3e2..68b84b9c4d 100644 --- a/products/ufs/internal/ufs/create.go +++ b/products/ufs/internal/ufs/create.go @@ -38,14 +38,15 @@ func newCreate(ctx *cli.Context) *cobra.Command { req.Size = flags.Int("size-gb", 100, "Required. Size of the UFS volume. Unit: GB") req.StorageType = flags.String("storage-type", "Basic", "Optional. Storage type: 'Basic' (capacity) or 'Advanced' (performance)") req.ProtocolType = flags.String("protocol-type", "NFS", "Optional. Protocol type: 'NFS' or 'SMB'") - req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") - req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") - req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") req.ChargeType = flags.String("charge-type", "Dynamic", "Optional. 'Year', pay yearly; 'Month', pay monthly; 'Dynamic', pay hourly") req.Quantity = flags.Int("quantity", 1, "Optional. The duration of the instance. N years/months") req.Tag = flags.String("group", "Default", "Optional. Business group") req.Remark = flags.String("remark", "", "Optional. Remark") + ctx.BindRegion(cmd, req) + ctx.BindZone(cmd, req) + ctx.BindProjectID(cmd, req) + command.SetFlagValues(cmd, "charge-type", "Month", "Year", "Dynamic", "Trial") command.SetFlagValues(cmd, "storage-type", "Basic", "Advanced") command.SetFlagValues(cmd, "protocol-type", "NFS", "SMB") diff --git a/products/ufs/internal/ufs/delete.go b/products/ufs/internal/ufs/delete.go index ec68bc12ad..d472b2d389 100644 --- a/products/ufs/internal/ufs/delete.go +++ b/products/ufs/internal/ufs/delete.go @@ -49,11 +49,10 @@ func newDelete(ctx *cli.Context) *cobra.Command { flags := cmd.Flags() flags.SortFlags = false volumeIDs = flags.StringSlice("volume-id", nil, "Required. The Resource ID of UFS volumes to delete") - req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") - req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") - req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") yes = flags.BoolP("yes", "y", false, "Optional. Do not prompt for confirmation.") + ctx.BindCommonParams(cmd, req) + cmd.MarkFlagRequired("volume-id") return cmd diff --git a/products/ufs/internal/ufs/describe.go b/products/ufs/internal/ufs/describe.go index 9fec2c2f5a..afa7eb4ad7 100644 --- a/products/ufs/internal/ufs/describe.go +++ b/products/ufs/internal/ufs/describe.go @@ -48,13 +48,14 @@ func newDescribe(ctx *cli.Context) *cobra.Command { } flags := cmd.Flags() flags.SortFlags = false - req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") - req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") - req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") req.VolumeId = flags.String("volume-id", "", "Optional. Resource ID of the UFS volume") req.Limit = flags.Int("limit", 50, "Optional. Limit") req.Offset = flags.Int("offset", 0, "Optional. Offset") + ctx.BindRegion(cmd, req) + ctx.BindZone(cmd, req) + ctx.BindProjectID(cmd, req) + return cmd } diff --git a/products/ufs/testdata/cmdtree.golden b/products/ufs/testdata/cmdtree.golden index bbed2886aa..51a05eda54 100644 --- a/products/ufs/testdata/cmdtree.golden +++ b/products/ufs/testdata/cmdtree.golden @@ -1,6 +1,5 @@ ucloud ufs use=ufs short=Manage UFS (UCloud File Storage) volumes ucloud ufs create use=create short=Create a UFS volume - flag=async short= default=false required= flag=charge-type short= default=Dynamic required= flag=group short= default=Default required= flag=name short= default= required=true @@ -10,7 +9,7 @@ ucloud ufs create use=create short=Create a UFS volume flag=region short= default= required= flag=remark short= default= required= flag=size-gb short= default=100 required=true - flag=storage-type short= default=SATA required= + flag=storage-type short= default=Basic required= flag=zone short= default= required= ucloud ufs delete use=delete short=Delete UFS volume(s) flag=project-id short= default= required= diff --git a/products/ufs/testdata/completion.golden b/products/ufs/testdata/completion.golden index 9c19754e79..d8717824cf 100644 --- a/products/ufs/testdata/completion.golden +++ b/products/ufs/testdata/completion.golden @@ -1,4 +1,12 @@ ucloud ufs create charge-type static Dynamic,Month,Trial,Year +ucloud ufs create project-id dynamic ucloud ufs create protocol-type static NFS,SMB +ucloud ufs create region dynamic ucloud ufs create storage-type static Advanced,Basic -ucloud ufs delete volume-id dynamic +ucloud ufs create zone dynamic +ucloud ufs delete project-id dynamic +ucloud ufs delete region dynamic +ucloud ufs delete zone dynamic +ucloud ufs describe project-id dynamic +ucloud ufs describe region dynamic +ucloud ufs describe zone dynamic diff --git a/products/upfs/internal/upfs/create.go b/products/upfs/internal/upfs/create.go index c700cacebe..1bc20a62c1 100644 --- a/products/upfs/internal/upfs/create.go +++ b/products/upfs/internal/upfs/create.go @@ -37,14 +37,15 @@ func newCreate(ctx *cli.Context) *cobra.Command { req.VolumeName = flags.String("name", "", "Required. Name of the UPFS volume to create") req.Size = flags.Int("size-gb", 500, "Required. Size of the UPFS volume. Unit: GB, must be a multiple of 100, minimum 500") req.ProtocolType = flags.String("protocol-type", "POSIX", "Optional. Protocol type, currently only supports POSIX") - req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") - req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") - req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") req.ChargeType = flags.String("charge-type", "Dynamic", "Optional. 'Year', pay yearly; 'Month', pay monthly; 'Dynamic', pay hourly") req.Quantity = flags.Int("quantity", 1, "Optional. The duration of the instance. N years/months") req.Tag = flags.String("group", "Default", "Optional. Business group") req.Remark = flags.String("remark", "", "Optional. Remark") + ctx.BindRegion(cmd, req) + ctx.BindZone(cmd, req) + ctx.BindProjectID(cmd, req) + command.SetFlagValues(cmd, "charge-type", "Month", "Year", "Dynamic", "Trial") command.SetFlagValues(cmd, "protocol-type", "POSIX") diff --git a/products/upfs/internal/upfs/delete.go b/products/upfs/internal/upfs/delete.go index 3c14599c21..bdad553f38 100644 --- a/products/upfs/internal/upfs/delete.go +++ b/products/upfs/internal/upfs/delete.go @@ -49,11 +49,10 @@ func newDelete(ctx *cli.Context) *cobra.Command { flags := cmd.Flags() flags.SortFlags = false volumeIDs = flags.StringSlice("volume-id", nil, "Required. The Resource ID of UPFS volumes to delete") - req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") - req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") - req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") yes = flags.BoolP("yes", "y", false, "Optional. Do not prompt for confirmation.") + ctx.BindCommonParams(cmd, req) + cmd.MarkFlagRequired("volume-id") return cmd diff --git a/products/upfs/internal/upfs/describe.go b/products/upfs/internal/upfs/describe.go index 5de882b0ee..fc9f779672 100644 --- a/products/upfs/internal/upfs/describe.go +++ b/products/upfs/internal/upfs/describe.go @@ -47,13 +47,14 @@ func newDescribe(ctx *cli.Context) *cobra.Command { } flags := cmd.Flags() flags.SortFlags = false - req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") - req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") - req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") req.VolumeId = flags.String("volume-id", "", "Optional. Resource ID of the UPFS volume") req.Limit = flags.Int("limit", 50, "Optional. Limit") req.Offset = flags.Int("offset", 0, "Optional. Offset") + ctx.BindRegion(cmd, req) + ctx.BindZone(cmd, req) + ctx.BindProjectID(cmd, req) + return cmd } diff --git a/products/upfs/product.yaml b/products/upfs/product.yaml index 2b0d01c02f..c61f9f05e1 100644 --- a/products/upfs/product.yaml +++ b/products/upfs/product.yaml @@ -1,7 +1,7 @@ # products/upfs/product.yaml — upfs 产品元数据(归属真源,owner 自治维护) name: upfs owners: - - pearlin-pan + - pearlinpan commands: - upfs enabled: true diff --git a/products/upfs/testdata/cmdtree.golden b/products/upfs/testdata/cmdtree.golden index eb42cd8a91..0267eaba7f 100644 --- a/products/upfs/testdata/cmdtree.golden +++ b/products/upfs/testdata/cmdtree.golden @@ -1,6 +1,5 @@ ucloud upfs use=upfs short=Manage UPFS (UCloud Parallel File Storage) volumes ucloud upfs create use=create short=Create a UPFS volume - flag=async short= default=false required= flag=charge-type short= default=Dynamic required= flag=group short= default=Default required= flag=name short= default= required=true diff --git a/products/upfs/testdata/completion.golden b/products/upfs/testdata/completion.golden index c11a9c49a7..6740e8038b 100644 --- a/products/upfs/testdata/completion.golden +++ b/products/upfs/testdata/completion.golden @@ -1,3 +1,11 @@ ucloud upfs create charge-type static Dynamic,Month,Trial,Year +ucloud upfs create project-id dynamic ucloud upfs create protocol-type static POSIX -ucloud upfs delete volume-id dynamic +ucloud upfs create region dynamic +ucloud upfs create zone dynamic +ucloud upfs delete project-id dynamic +ucloud upfs delete region dynamic +ucloud upfs delete zone dynamic +ucloud upfs describe project-id dynamic +ucloud upfs describe region dynamic +ucloud upfs describe zone dynamic diff --git a/products/usnap/internal/usnap/create.go b/products/usnap/internal/usnap/create.go index 1e117379a4..50e6e3fb87 100644 --- a/products/usnap/internal/usnap/create.go +++ b/products/usnap/internal/usnap/create.go @@ -44,14 +44,15 @@ func newCreate(ctx *cli.Context) *cobra.Command { req.Day = flags.Int("backup-day", 0, "Optional. Backup day range") req.Hour = flags.Int("backup-hour", 0, "Optional. Backup hour") req.Journal = flags.Int("journal", 0, "Optional. Journal retention count") - req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") - req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") - req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") req.ChargeType = flags.String("charge-type", "Dynamic", "Optional. 'Year', pay yearly; 'Month', pay monthly; 'Dynamic', pay hourly") req.Quantity = flags.Int("quantity", 1, "Optional. The duration of the instance. N years/months") req.Tag = flags.String("group", "Default", "Optional. Business group") async = flags.Bool("async", false, "Optional. Do not wait for the long-running operation to finish.") + ctx.BindRegion(cmd, req) + ctx.BindZone(cmd, req) + ctx.BindProjectID(cmd, req) + command.SetFlagValues(cmd, "charge-type", "Month", "Year", "Dynamic", "Trial") cmd.MarkFlagRequired("vdisk-id") diff --git a/products/usnap/internal/usnap/delete.go b/products/usnap/internal/usnap/delete.go index 14e43861bb..2d5d4a37a7 100644 --- a/products/usnap/internal/usnap/delete.go +++ b/products/usnap/internal/usnap/delete.go @@ -44,11 +44,10 @@ func newDelete(ctx *cli.Context) *cobra.Command { flags := cmd.Flags() flags.SortFlags = false req.VDiskId = flags.String("vdisk-id", "", "Required. Resource ID of the disk whose snapshot service to delete") - req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") - req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") - req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") yes = flags.BoolP("yes", "y", false, "Optional. Do not prompt for confirmation.") + ctx.BindCommonParams(cmd, req) + cmd.MarkFlagRequired("vdisk-id") return cmd diff --git a/products/usnap/internal/usnap/describe.go b/products/usnap/internal/usnap/describe.go index caa1a388ed..548318b020 100644 --- a/products/usnap/internal/usnap/describe.go +++ b/products/usnap/internal/usnap/describe.go @@ -49,15 +49,16 @@ func newDescribe(ctx *cli.Context) *cobra.Command { } flags := cmd.Flags() flags.SortFlags = false - req.ProjectId = flags.String("project-id", ctx.DefaultProjectID(), "Optional. Assign project-id") - req.Region = flags.String("region", ctx.DefaultRegion(), "Optional. Assign region") - req.Zone = flags.String("zone", ctx.DefaultZone(), "Optional. Assign availability zone") req.SnapshotServiceId = flags.String("service-id", "", "Optional. Resource ID of the snapshot service") req.VDiskId = flags.String("vdisk-id", "", "Optional. Resource ID of the disk") req.SnapshotId = flags.String("snapshot-id", "", "Optional. Resource ID of the snapshot") req.Limit = flags.Int("limit", 50, "Optional. Limit") req.Offset = flags.Int("offset", 0, "Optional. Offset") + ctx.BindRegion(cmd, req) + ctx.BindZone(cmd, req) + ctx.BindProjectID(cmd, req) + return cmd } diff --git a/products/usnap/product.yaml b/products/usnap/product.yaml index 112a31cc54..91e9528116 100644 --- a/products/usnap/product.yaml +++ b/products/usnap/product.yaml @@ -1,7 +1,7 @@ # products/usnap/product.yaml — usnap 产品元数据(归属真源,owner 自治维护) name: usnap owners: - - pearlin-pan + - pearlinpan commands: - usnap enabled: true diff --git a/products/usnap/testdata/completion.golden b/products/usnap/testdata/completion.golden index 75c4c5dc49..81b490fe24 100644 --- a/products/usnap/testdata/completion.golden +++ b/products/usnap/testdata/completion.golden @@ -1,2 +1,10 @@ ucloud usnap create charge-type static Dynamic,Month,Trial,Year -ucloud usnap delete vdisk-id dynamic +ucloud usnap create project-id dynamic +ucloud usnap create region dynamic +ucloud usnap create zone dynamic +ucloud usnap delete project-id dynamic +ucloud usnap delete region dynamic +ucloud usnap delete zone dynamic +ucloud usnap describe project-id dynamic +ucloud usnap describe region dynamic +ucloud usnap describe zone dynamic From f42cc96f4014de0f52e98b499414ab2995b9309f Mon Sep 17 00:00:00 2001 From: pearlinpan Date: Thu, 9 Jul 2026 17:41:07 +0800 Subject: [PATCH 3/4] Update product.yaml --- products/ufs/product.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/products/ufs/product.yaml b/products/ufs/product.yaml index 4693081e69..9653453d52 100644 --- a/products/ufs/product.yaml +++ b/products/ufs/product.yaml @@ -1,7 +1,7 @@ # products/ufs/product.yaml — ufs 产品元数据(归属真源,owner 自治维护) name: ufs owners: - - Episkey-G + - pearlinpan commands: - ufs enabled: true From 1d8306a425cbc8c579a53e1c17096904397a4cd0 Mon Sep 17 00:00:00 2001 From: pearlinpan Date: Thu, 9 Jul 2026 17:41:38 +0800 Subject: [PATCH 4/4] Update product.yaml --- products/udisk/product.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/products/udisk/product.yaml b/products/udisk/product.yaml index b77f48efa3..66e2b8ba60 100644 --- a/products/udisk/product.yaml +++ b/products/udisk/product.yaml @@ -1,7 +1,7 @@ # products/udisk/product.yaml — udisk 产品元数据(归属真源,owner 自治维护) name: udisk owners: - - Episkey-G + - pearlinpan commands: - udisk enabled: true