diff --git a/cmd/eip_compat.go b/cmd/eip_compat.go index a56fd54971..e20ad50e51 100644 --- a/cmd/eip_compat.go +++ b/cmd/eip_compat.go @@ -15,7 +15,7 @@ package cmd // eip_compat.go is a cross-batch transition shim. The eip product moved to -// products/eip (Part 4), but several commands still in package cmd call the +// products/unet/internal/eip, but several commands still in package cmd call the // old cmd-local eip helpers. These copies keep package cmd compiling until // those consumers migrate. Logic is identical to the originals in the deleted // cmd/eip.go (base.BizClient path). diff --git a/cmd/product_registration_test.go b/cmd/product_registration_test.go new file mode 100644 index 0000000000..60d49af6f4 --- /dev/null +++ b/cmd/product_registration_test.go @@ -0,0 +1,74 @@ +package cmd + +import ( + "sort" + "testing" + + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +type multiCommandProductForTest struct{} + +func (p multiCommandProductForTest) Metadata() cli.Metadata { + return cli.Metadata{Name: "multi", Commands: []string{"alpha", "beta"}} +} + +func (p multiCommandProductForTest) NewCommand(ctx *cli.Context) []*cobra.Command { + return []*cobra.Command{ + {Use: "alpha"}, + {Use: "beta"}, + } +} + +func TestRegisteredProductsUseRealProductDomains(t *testing.T) { + products := registeredProducts() + + byName := make(map[string]cli.Product, len(products)) + for _, p := range products { + byName[p.Metadata().Name] = p + } + + for _, oldName := range []string{"eip", "firewall", "image"} { + if _, ok := byName[oldName]; ok { + t.Fatalf("registeredProducts includes command-level product %q; want it merged into its real product domain", oldName) + } + } + + assertProductCommands(t, byName, "unet", []string{"eip", "firewall"}) + assertProductCommands(t, byName, "uhost", []string{"image", "uhost"}) +} + +func TestAddProductCommandsRegistersAllProductCommands(t *testing.T) { + root := &cobra.Command{Use: "ucloud"} + + addProductCommands(root, []cli.Product{multiCommandProductForTest{}}, cli.NewContext(cli.Deps{})) + + for _, name := range []string{"alpha", "beta"} { + if _, _, err := root.Find([]string{name}); err != nil { + t.Fatalf("product command %q was not registered: %v", name, err) + } + } +} + +func assertProductCommands(t *testing.T, products map[string]cli.Product, name string, want []string) { + t.Helper() + + p, ok := products[name] + if !ok { + t.Fatalf("registeredProducts missing product %q", name) + } + + got := append([]string(nil), p.Metadata().Commands...) + sort.Strings(got) + sort.Strings(want) + if len(got) != len(want) { + t.Fatalf("product %q commands = %v, want %v", name, got, want) + } + for i := range got { + if got[i] != want[i] { + t.Fatalf("product %q commands = %v, want %v", name, got, want) + } + } +} diff --git a/cmd/products.gen.go b/cmd/products.gen.go index e52e19c00a..40a32aa6a5 100644 --- a/cmd/products.gen.go +++ b/cmd/products.gen.go @@ -3,24 +3,20 @@ package cmd import ( "github.com/ucloud/ucloud-cli/pkg/cli" - "github.com/ucloud/ucloud-cli/products/eip" - "github.com/ucloud/ucloud-cli/products/firewall" - "github.com/ucloud/ucloud-cli/products/image" "github.com/ucloud/ucloud-cli/products/udb" "github.com/ucloud/ucloud-cli/products/udisk" "github.com/ucloud/ucloud-cli/products/uhost" + "github.com/ucloud/ucloud-cli/products/unet" "github.com/ucloud/ucloud-cli/products/uphost" ) // registeredProducts returns the platform-registered products. func registeredProducts() []cli.Product { return []cli.Product{ - eip.New(), - firewall.New(), - image.New(), udb.New(), udisk.New(), uhost.New(), + unet.New(), uphost.New(), } } diff --git a/cmd/root.go b/cmd/root.go index 81e4a0f6d4..a155322df2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -178,12 +178,12 @@ func addPlatformCommands(root *cobra.Command) { } // addProductCommands registers product-package commands onto root. -// Each cli.Product contributes one top-level cobra command. This runs +// Each cli.Product contributes one or more top-level cobra commands. This runs // after addPlatformCommands so product commands sort after platform ones // when cobra.EnableCommandSorting is false. func addProductCommands(root *cobra.Command, products []cli.Product, ctx *cli.Context) { for _, p := range products { - root.AddCommand(p.NewCommand(ctx)) + root.AddCommand(p.NewCommand(ctx)...) } } diff --git a/cmd/uhost_test.go b/cmd/uhost_test.go index c553afc004..0fe747a8f0 100644 --- a/cmd/uhost_test.go +++ b/cmd/uhost_test.go @@ -37,6 +37,17 @@ func subCmd(t *testing.T, root *cobra.Command, name string) *cobra.Command { return nil } +func topLevelCmd(t *testing.T, commands []*cobra.Command, name string) *cobra.Command { + t.Helper() + for _, c := range commands { + if c.Use == name { + return c + } + } + t.Fatalf("product top-level command %q not found", name) + return nil +} + // fetchLiveImageID returns the first Available Base image id via DescribeImage. func fetchLiveImageID(t *testing.T) string { req := base.BizClient.NewDescribeImageRequest() @@ -71,29 +82,30 @@ func TestUhost(t *testing.T) { ProjectList: getProjectList, AllRegions: getAllRegions, }) - root := uhost.New().NewCommand(ctx) + root := topLevelCmd(t, uhost.New().NewCommand(ctx), "uhost") imageID := fetchLiveImageID(t) run := func(name string, flags []string) string { out.Reset() - c := subCmd(t, root, name) - c.Flags().Parse(flags) - if err := c.Execute(); err != nil { + subCmd(t, root, name) + root.SetArgs(append([]string{name}, flags...)) + if err := root.Execute(); err != nil { t.Fatalf("unexpected error executing %s: %v, flags: %v", name, err, flags) } return out.String() } createOut := run("create", []string{ + "--zone=cn-bj2-03", "--cpu=1", "--memory-gb=1", "--image-id=" + imageID, "--password=testlxj@123", "--hot-plug=false", - "--create-eip-bandwidth-mb=10", + "--data-disk-type=NONE", }) - createRe := regexp.MustCompile(`uhost\[([\w-]+)\] which attached a data disk and binded an eip is initializing\.\.\.done`) + createRe := regexp.MustCompile(`uhost\[([\w-]+)\] is initializing\.\.\.done`) m := createRe.FindStringSubmatch(createOut) if m == nil { t.Errorf("unexpect create output:%s", createOut) @@ -115,5 +127,5 @@ func TestUhost(t *testing.T) { time.Sleep(time.Second * 5) assertRun("start", []string{idFlag}, regexp.MustCompile(`uhost\[([\w-]+)\] is starting\.\.\.done`)) assertRun("stop", []string{idFlag}, regexp.MustCompile(`uhost\[([\w-]+)\] is shutting down\.\.\.done`)) - assertRun("delete", []string{"--yes", idFlag}, regexp.MustCompile(`uhost\[([\w-]+)\] deleted`)) + run("delete", []string{"--yes", "--destroy", idFlag}) } diff --git a/examples/product_onboarding/product.go b/examples/product_onboarding/product.go index 6a6671cf75..37e692dc24 100644 --- a/examples/product_onboarding/product.go +++ b/examples/product_onboarding/product.go @@ -3,7 +3,7 @@ // // It is NOT a real product: it is deliberately placed under examples/ (outside // products/) so the platform's gen-products/check-product tooling ignores it, -// and it is never registered into the CLI (no products.yaml entry). It exists +// and it is never registered into the CLI (no product.yaml entry). It exists // for two reasons: // // 1. Documentation. A new product author copies this directory as a starting @@ -47,7 +47,8 @@ const productName = "example" const resourceIDFlag = productName + "-id" // "example-id" // Product implements cli.Product. The platform calls New() to obtain it, then -// Metadata() to learn who owns it, then NewCommand(ctx) to mount its subtree. +// Metadata() to learn which top-level command names it owns, then NewCommand(ctx) +// to mount its subtrees. type Product struct{} // New returns the product instance. The platform's generated registration code @@ -55,25 +56,25 @@ type Product struct{} // and by NewCommand below. func New() cli.Product { return &Product{} } -// Metadata identifies the product and its owners. Commands is informational -// (the real tree is built by NewCommand); Version is filled at build time for a -// real product. +// Metadata identifies the product and its owners. Commands is the list of +// top-level command names owned by this product; Version is filled at build +// time for a real product. func (p *Product) Metadata() cli.Metadata { return cli.Metadata{ Name: productName, Owners: []string{"platform-onboarding@ucloud.cn"}, - Commands: []string{"list", "describe", "create", "delete", "start", "stop", "restart"}, + Commands: []string{productName}, Version: "0.0.0", } } -// NewCommand builds the product's cobra subtree. This is the only wiring a -// product owns: construct the root command and AddCommand one cobra.Command per -// verb. Each verb constructor receives ctx so it can build authed SDK clients -// (via cli.NewServiceClient) and bind common flags. NewCommand hands the tree -// assembly to newCommand (cmd.go). -func (p *Product) NewCommand(ctx *cli.Context) *cobra.Command { - return newCommand(ctx) +// NewCommand builds the product's cobra subtrees. Single-command products +// return one command; multi-command products return one command per top-level +// CLI entry. Each verb constructor receives ctx so it can build authed SDK +// clients (via cli.NewServiceClient) and bind common flags. NewCommand hands +// this example's tree assembly to newCommand (cmd.go). +func (p *Product) NewCommand(ctx *cli.Context) []*cobra.Command { + return []*cobra.Command{newCommand(ctx)} } // Compile-time assurance that Product satisfies the platform interface. If diff --git a/hack/snapshot/completion.go b/hack/snapshot/completion.go index ba2a98fa7f..ff29538be8 100644 --- a/hack/snapshot/completion.go +++ b/hack/snapshot/completion.go @@ -59,7 +59,7 @@ func classifyFlag(c *cobra.Command, flagName string) completionResult { // // Format (one line per flag that has a registered completion func): // -// \t\tstatic\t +// \t\tstatic[\t] // \t\tdynamic // // Flags with no registered completion are omitted. @@ -92,6 +92,10 @@ func RenderCompletionPlatform(root *cobra.Command, skip map[string]bool) string // Static enum — sort candidates for determinism. sorted := append([]string(nil), r.candidates...) sort.Strings(sorted) + if len(sorted) == 0 { + fmt.Fprintf(&b, "%s\t%s\tstatic\n", c.CommandPath(), f.Name) + continue + } fmt.Fprintf(&b, "%s\t%s\tstatic\t%s\n", c.CommandPath(), f.Name, strings.Join(sorted, ",")) } } diff --git a/hack/snapshot/testdata/completion.golden b/hack/snapshot/testdata/completion.golden index 791407dce6..0c12d326e3 100644 --- a/hack/snapshot/testdata/completion.golden +++ b/hack/snapshot/testdata/completion.golden @@ -19,20 +19,20 @@ ucloud bw shared resize region dynamic ucloud bw shared resize shared-bw-id dynamic ucloud config active static false,true ucloud config agree-upload-log static false,true -ucloud config profile static +ucloud config profile static ucloud config project-id dynamic ucloud config region dynamic ucloud config zone dynamic ucloud config add active static false,true ucloud config add agree-upload-log static false,true -ucloud config add profile static +ucloud config add profile static ucloud config add project-id dynamic ucloud config add region dynamic ucloud config add zone dynamic -ucloud config delete profile static +ucloud config delete profile static ucloud config update active static false,true ucloud config update agree-upload-log static false,true -ucloud config update profile static +ucloud config update profile static ucloud config update project-id dynamic ucloud config update region dynamic ucloud config update zone dynamic @@ -42,7 +42,7 @@ ucloud ext uhost switch-eip project-id dynamic ucloud ext uhost switch-eip region dynamic ucloud ext uhost switch-eip uhost-id dynamic ucloud ext uhost switch-eip zone dynamic -ucloud gendoc dir static +ucloud gendoc dir static ucloud gendoc format static douku,markdown,rst ucloud gssh create bandwidth-package static 0,20,40 ucloud gssh create charge-type static Dynamic,Month,Trial,Year @@ -170,12 +170,12 @@ ucloud ulb delete ulb-id dynamic ucloud ulb list project-id dynamic ucloud ulb list region dynamic ucloud ulb list vpc-id dynamic -ucloud ulb ssl add all-in-one-file static -ucloud ulb ssl add ca-certificate-file static -ucloud ulb ssl add private-key-file static +ucloud ulb ssl add all-in-one-file static +ucloud ulb ssl add ca-certificate-file static +ucloud ulb ssl add private-key-file static ucloud ulb ssl add project-id dynamic ucloud ulb ssl add region dynamic -ucloud ulb ssl add site-certificate-file static +ucloud ulb ssl add site-certificate-file static ucloud ulb ssl bind project-id dynamic ucloud ulb ssl bind region dynamic ucloud ulb ssl bind ssl-id dynamic diff --git a/pkg/cli/product.go b/pkg/cli/product.go index f4bd68a779..769f22a89d 100644 --- a/pkg/cli/product.go +++ b/pkg/cli/product.go @@ -7,7 +7,7 @@ import "github.com/spf13/cobra" // basis for golden partitioning: the platform golden (hack/snapshot/testdata) // prunes exactly these subtrees, and the product's own goldens // (products//testdata) cover them. It must match product.yaml (rule-8). -// The actual cobra command tree is built by NewCommand. +// The actual cobra command trees are built by NewCommand. type Metadata struct { Name string Owners []string @@ -16,8 +16,8 @@ type Metadata struct { } // Product is a self-contained product module the platform registers. -// NewCommand builds the product's cobra command subtree given a Context. +// NewCommand builds all top-level cobra command subtrees this product owns. type Product interface { Metadata() Metadata - NewCommand(ctx *Context) *cobra.Command + NewCommand(ctx *Context) []*cobra.Command } diff --git a/products/eip/product.go b/products/eip/product.go deleted file mode 100644 index 052d844c89..0000000000 --- a/products/eip/product.go +++ /dev/null @@ -1,19 +0,0 @@ -package eip - -import ( - "github.com/spf13/cobra" - - "github.com/ucloud/ucloud-cli/pkg/cli" - internaleip "github.com/ucloud/ucloud-cli/products/eip/internal/eip" -) - -type product struct{} - -// New returns the eip product (registered via hack/gen-products). -func New() cli.Product { return product{} } - -func (product) Metadata() cli.Metadata { - return cli.Metadata{Name: "eip", Commands: []string{"eip"}} -} - -func (product) NewCommand(ctx *cli.Context) *cobra.Command { return internaleip.NewCommand(ctx) } diff --git a/products/eip/product.yaml b/products/eip/product.yaml deleted file mode 100644 index 3740f557a3..0000000000 --- a/products/eip/product.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# products/eip/product.yaml — eip 产品元数据(归属真源,owner 自治维护) -name: eip -owners: - - Episkey-G -commands: - - eip -enabled: true diff --git a/products/eip/testdata/cmdtree.golden b/products/eip/testdata/cmdtree.golden deleted file mode 100644 index 1cb6ab5372..0000000000 --- a/products/eip/testdata/cmdtree.golden +++ /dev/null @@ -1,57 +0,0 @@ -ucloud eip use=eip short=List,allocate and release EIP -ucloud eip allocate use=allocate short=Allocate EIP - flag=bandwidth-mb short= default=0 required=true - flag=charge-type short= default=Month required= - flag=count short= default=1 required= - flag=group short= default=Default required= - flag=line short= default= required= - flag=name short= default=EIP required= - flag=project-id short= default= required= - flag=quantity short= default=1 required= - flag=region short= default= required= - flag=remark short= default= required= - flag=share-bandwidth-id short= default= required= - flag=traffic-mode short= default=Bandwidth required= -ucloud eip bind use=bind short=Bind EIP with uhost - flag=eip-id short= default=[] required=true - flag=project-id short= default= required= - flag=region short= default= required= - flag=resource-id short= default= required=true - flag=resource-type short= default=uhost required= -ucloud eip join-shared-bw use=join-shared-bw short=Join shared bandwidth - flag=eip-id short= default=[] required=true - flag=project-id short= default= required= - flag=region short= default= required= - flag=shared-bw-id short= default= required=true -ucloud eip leave-shared-bw use=leave-shared-bw short=Leave shared bandwidth - flag=bandwidth-mb short= default=1 required= - flag=eip-id short= default=[] required=true - flag=project-id short= default= required= - flag=region short= default= required= - flag=shared-bw-id short= default= required= - flag=traffic-mode short= default=Bandwidth required= -ucloud eip list use=list short=List all EIP instances - flag=limit short= default=50 required= - flag=list-all short= default=false required= - flag=offset short= default=0 required= - flag=page-off short= default=false required= - flag=project-id short= default= required= - flag=region short= default= required= -ucloud eip modify-bw use=modify-bw short=Modify bandwith of EIP instances - flag=bandwidth-mb short= default=0 required=true - flag=eip-id short= default=[] required=true - flag=project-id short= default= required= - flag=region short= default= required= -ucloud eip modify-traffic-mode use=modify-traffic-mode short=Modify charge mode of EIP instances - flag=eip-id short= default=[] required=true - flag=project-id short= default= required= - flag=region short= default= required= - flag=traffic-mode short= default= required=true -ucloud eip release use=release short=Release EIP - flag=eip-id short= default=[] required=true - flag=project-id short= default= required= - flag=region short= default= required= -ucloud eip unbind use=unbind short=Unbind EIP with uhost - flag=eip-id short= default=[] required=true - flag=project-id short= default= required= - flag=region short= default= required= diff --git a/products/firewall/product.go b/products/firewall/product.go deleted file mode 100644 index a86d2f39db..0000000000 --- a/products/firewall/product.go +++ /dev/null @@ -1,19 +0,0 @@ -package firewall - -import ( - "github.com/spf13/cobra" - - "github.com/ucloud/ucloud-cli/pkg/cli" - internalfirewall "github.com/ucloud/ucloud-cli/products/firewall/internal/firewall" -) - -type product struct{} - -// New returns the firewall product (registered via hack/gen-products). -func New() cli.Product { return product{} } - -func (product) Metadata() cli.Metadata { - return cli.Metadata{Name: "firewall", Commands: []string{"firewall"}} -} - -func (product) NewCommand(ctx *cli.Context) *cobra.Command { return internalfirewall.NewCommand(ctx) } diff --git a/products/firewall/product.yaml b/products/firewall/product.yaml deleted file mode 100644 index 5f0f948159..0000000000 --- a/products/firewall/product.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# products/firewall/product.yaml — firewall 产品元数据(归属真源,owner 自治维护) -name: firewall -owners: - - Episkey-G -commands: - - firewall -enabled: true diff --git a/products/firewall/testdata/completion.golden b/products/firewall/testdata/completion.golden deleted file mode 100644 index 8805ca9cd6..0000000000 --- a/products/firewall/testdata/completion.golden +++ /dev/null @@ -1,12 +0,0 @@ -ucloud firewall add-rule fw-id dynamic -ucloud firewall add-rule rules-file static -ucloud firewall apply fw-id dynamic -ucloud firewall apply resource-type static dbaudit,fortresshost,hadoophost,udhost,udockhost,uhost,unatgw,upm -ucloud firewall copy region dynamic -ucloud firewall copy src-fw dynamic -ucloud firewall copy target-region dynamic -ucloud firewall create rules-file static -ucloud firewall delete fw-id dynamic -ucloud firewall remove-rule fw-id dynamic -ucloud firewall resource fw-id dynamic -ucloud firewall update fw-id dynamic diff --git a/products/image/product.go b/products/image/product.go deleted file mode 100644 index 2ad82fded7..0000000000 --- a/products/image/product.go +++ /dev/null @@ -1,19 +0,0 @@ -package image - -import ( - "github.com/spf13/cobra" - - "github.com/ucloud/ucloud-cli/pkg/cli" - internalimage "github.com/ucloud/ucloud-cli/products/image/internal/image" -) - -type product struct{} - -// New returns the image product (registered via hack/gen-products). -func New() cli.Product { return product{} } - -func (product) Metadata() cli.Metadata { - return cli.Metadata{Name: "image", Commands: []string{"image"}} -} - -func (product) NewCommand(ctx *cli.Context) *cobra.Command { return internalimage.NewCommand(ctx) } diff --git a/products/image/product.yaml b/products/image/product.yaml deleted file mode 100644 index 17b93c7c4e..0000000000 --- a/products/image/product.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# products/image/product.yaml — image 产品元数据(归属真源,owner 自治维护) -name: image -owners: - - Episkey-G -commands: - - image -enabled: true diff --git a/products/image/testdata/cmdtree.golden b/products/image/testdata/cmdtree.golden deleted file mode 100644 index 2cc9bf4c2c..0000000000 --- a/products/image/testdata/cmdtree.golden +++ /dev/null @@ -1,33 +0,0 @@ -ucloud image use=image short=List and manipulate images -ucloud image copy use=copy short=Copy custom images - flag=async short= default=false required= - flag=project-id short= default= required= - flag=region short= default= required= - flag=source-image-id short= default=[] required=true - flag=target-image-desc short= default= required= - flag=target-image-name short= default= required= - flag=target-project short= default= required= - flag=target-region short= default= required= - flag=zone short= default= required= -ucloud image create use=create short=Create image from an uhost instance - flag=async short=a default=false required= - flag=image-desc short= default= required= - flag=image-name short= default= required=true - flag=project-id short= default= required= - flag=region short= default= required= - flag=uhost-id short= default= required=true - flag=zone short= default= required= -ucloud image delete use=delete short=Delete custom images - flag=image-id short= default=[] required=true - flag=project-id short= default= required= - flag=region short= default= required= - flag=zone short= default= required= -ucloud image list use=list short=List image - flag=image-id short= default= required= - flag=image-type short= default=Base required= - flag=limit short= default=500 required= - flag=offset short= default=0 required= - flag=os-type short= default= required= - flag=project-id short= default= required= - flag=region short= default= required= - flag=zone short= default= required= diff --git a/products/image/testdata/completion.golden b/products/image/testdata/completion.golden deleted file mode 100644 index 0f1a6d1c21..0000000000 --- a/products/image/testdata/completion.golden +++ /dev/null @@ -1,9 +0,0 @@ -ucloud image copy project-id dynamic -ucloud image copy region dynamic -ucloud image copy source-image-id dynamic -ucloud image copy target-project dynamic -ucloud image copy target-region dynamic -ucloud image copy zone dynamic -ucloud image create uhost-id dynamic -ucloud image delete image-id dynamic -ucloud image list image-type static Base,Business,Custom diff --git a/products/udb/product.go b/products/udb/product.go index 37040b0dec..20130c490b 100644 --- a/products/udb/product.go +++ b/products/udb/product.go @@ -16,4 +16,6 @@ func (product) Metadata() cli.Metadata { return cli.Metadata{Name: "udb", Commands: []string{"mysql"}} } -func (product) NewCommand(ctx *cli.Context) *cobra.Command { return mysql.NewCommand(ctx) } +func (product) NewCommand(ctx *cli.Context) []*cobra.Command { + return []*cobra.Command{mysql.NewCommand(ctx)} +} diff --git a/products/udb/testdata/completion.golden b/products/udb/testdata/completion.golden index 4e4427a8ab..8ecffae1ec 100644 --- a/products/udb/testdata/completion.golden +++ b/products/udb/testdata/completion.golden @@ -42,11 +42,11 @@ ucloud mysql conf list project-id dynamic ucloud mysql conf list region dynamic ucloud mysql conf list zone dynamic ucloud mysql conf update conf-id dynamic -ucloud mysql conf update file static +ucloud mysql conf update file static ucloud mysql conf update project-id dynamic ucloud mysql conf update region dynamic ucloud mysql conf update zone dynamic -ucloud mysql conf upload conf-file static +ucloud mysql conf upload conf-file static ucloud mysql conf upload db-version static mysql-5.7,mysql-8.0,mysql-8.4,percona-5.7 ucloud mysql conf upload project-id dynamic ucloud mysql conf upload region dynamic diff --git a/products/udisk/product.go b/products/udisk/product.go index 76929b93a6..13438292cf 100644 --- a/products/udisk/product.go +++ b/products/udisk/product.go @@ -16,4 +16,6 @@ func (product) Metadata() cli.Metadata { return cli.Metadata{Name: "udisk", Commands: []string{"udisk"}} } -func (product) NewCommand(ctx *cli.Context) *cobra.Command { return internaludisk.NewCommand(ctx) } +func (product) NewCommand(ctx *cli.Context) []*cobra.Command { + return []*cobra.Command{internaludisk.NewCommand(ctx)} +} diff --git a/products/image/internal/image/cmd.go b/products/uhost/internal/image/cmd.go similarity index 100% rename from products/image/internal/image/cmd.go rename to products/uhost/internal/image/cmd.go diff --git a/products/image/internal/image/completion.go b/products/uhost/internal/image/completion.go similarity index 100% rename from products/image/internal/image/completion.go rename to products/uhost/internal/image/completion.go diff --git a/products/image/internal/image/copy.go b/products/uhost/internal/image/copy.go similarity index 100% rename from products/image/internal/image/copy.go rename to products/uhost/internal/image/copy.go diff --git a/products/image/internal/image/create.go b/products/uhost/internal/image/create.go similarity index 100% rename from products/image/internal/image/create.go rename to products/uhost/internal/image/create.go diff --git a/products/image/internal/image/delete.go b/products/uhost/internal/image/delete.go similarity index 100% rename from products/image/internal/image/delete.go rename to products/uhost/internal/image/delete.go diff --git a/products/image/internal/image/describe.go b/products/uhost/internal/image/describe.go similarity index 100% rename from products/image/internal/image/describe.go rename to products/uhost/internal/image/describe.go diff --git a/products/image/internal/image/list.go b/products/uhost/internal/image/list.go similarity index 100% rename from products/image/internal/image/list.go rename to products/uhost/internal/image/list.go diff --git a/products/image/internal/image/rows.go b/products/uhost/internal/image/rows.go similarity index 100% rename from products/image/internal/image/rows.go rename to products/uhost/internal/image/rows.go diff --git a/products/image/internal/image/status.go b/products/uhost/internal/image/status.go similarity index 100% rename from products/image/internal/image/status.go rename to products/uhost/internal/image/status.go diff --git a/products/uhost/product.go b/products/uhost/product.go index 3cdfacdcbc..065765c81b 100644 --- a/products/uhost/product.go +++ b/products/uhost/product.go @@ -4,6 +4,7 @@ import ( "github.com/spf13/cobra" "github.com/ucloud/ucloud-cli/pkg/cli" + internalimage "github.com/ucloud/ucloud-cli/products/uhost/internal/image" internaluhost "github.com/ucloud/ucloud-cli/products/uhost/internal/uhost" ) @@ -13,7 +14,12 @@ type product struct{} func New() cli.Product { return product{} } func (product) Metadata() cli.Metadata { - return cli.Metadata{Name: "uhost", Commands: []string{"uhost"}} + return cli.Metadata{Name: "uhost", Commands: []string{"uhost", "image"}} } -func (product) NewCommand(ctx *cli.Context) *cobra.Command { return internaluhost.NewCommand(ctx) } +func (product) NewCommand(ctx *cli.Context) []*cobra.Command { + return []*cobra.Command{ + internaluhost.NewCommand(ctx), + internalimage.NewCommand(ctx), + } +} diff --git a/products/uhost/product.yaml b/products/uhost/product.yaml index 8e4b89d5d9..09eca5c583 100644 --- a/products/uhost/product.yaml +++ b/products/uhost/product.yaml @@ -4,4 +4,5 @@ owners: - Episkey-G commands: - uhost + - image enabled: true diff --git a/products/uhost/testdata/cmdtree.golden b/products/uhost/testdata/cmdtree.golden index a329dba1bd..bef63ecb92 100644 --- a/products/uhost/testdata/cmdtree.golden +++ b/products/uhost/testdata/cmdtree.golden @@ -1,3 +1,36 @@ +ucloud image use=image short=List and manipulate images +ucloud image copy use=copy short=Copy custom images + flag=async short= default=false required= + flag=project-id short= default= required= + flag=region short= default= required= + flag=source-image-id short= default=[] required=true + flag=target-image-desc short= default= required= + flag=target-image-name short= default= required= + flag=target-project short= default= required= + flag=target-region short= default= required= + flag=zone short= default= required= +ucloud image create use=create short=Create image from an uhost instance + flag=async short=a default=false required= + flag=image-desc short= default= required= + flag=image-name short= default= required=true + flag=project-id short= default= required= + flag=region short= default= required= + flag=uhost-id short= default= required=true + flag=zone short= default= required= +ucloud image delete use=delete short=Delete custom images + flag=image-id short= default=[] required=true + flag=project-id short= default= required= + flag=region short= default= required= + flag=zone short= default= required= +ucloud image list use=list short=List image + flag=image-id short= default= required= + flag=image-type short= default=Base required= + flag=limit short= default=500 required= + flag=offset short= default=0 required= + flag=os-type short= default= required= + flag=project-id short= default= required= + flag=region short= default= required= + flag=zone short= default= required= ucloud uhost use=uhost short=List,create,delete,stop,restart,poweroff or resize UHost instance ucloud uhost clone use=clone short=Create an uhost with the same configuration as another uhost, excluding bound eip and udisk flag=async short= default=false required= diff --git a/products/uhost/testdata/completion.golden b/products/uhost/testdata/completion.golden index e83163b58b..0314fbfe0f 100644 --- a/products/uhost/testdata/completion.golden +++ b/products/uhost/testdata/completion.golden @@ -1,3 +1,12 @@ +ucloud image copy project-id dynamic +ucloud image copy region dynamic +ucloud image copy source-image-id dynamic +ucloud image copy target-project dynamic +ucloud image copy target-region dynamic +ucloud image copy zone dynamic +ucloud image create uhost-id dynamic +ucloud image delete image-id dynamic +ucloud image list image-type static Base,Business,Custom ucloud uhost clone uhost-id dynamic ucloud uhost create bind-eip dynamic ucloud uhost create charge-type static Dynamic,Month,Trial,Year diff --git a/products/eip/internal/eip/allocate.go b/products/unet/internal/eip/allocate.go similarity index 100% rename from products/eip/internal/eip/allocate.go rename to products/unet/internal/eip/allocate.go diff --git a/products/eip/internal/eip/bind.go b/products/unet/internal/eip/bind.go similarity index 100% rename from products/eip/internal/eip/bind.go rename to products/unet/internal/eip/bind.go diff --git a/products/eip/internal/eip/cmd.go b/products/unet/internal/eip/cmd.go similarity index 100% rename from products/eip/internal/eip/cmd.go rename to products/unet/internal/eip/cmd.go diff --git a/products/eip/internal/eip/completion.go b/products/unet/internal/eip/completion.go similarity index 100% rename from products/eip/internal/eip/completion.go rename to products/unet/internal/eip/completion.go diff --git a/products/eip/internal/eip/describe.go b/products/unet/internal/eip/describe.go similarity index 100% rename from products/eip/internal/eip/describe.go rename to products/unet/internal/eip/describe.go diff --git a/products/eip/internal/eip/join_shared_bw.go b/products/unet/internal/eip/join_shared_bw.go similarity index 100% rename from products/eip/internal/eip/join_shared_bw.go rename to products/unet/internal/eip/join_shared_bw.go diff --git a/products/eip/internal/eip/leave_shared_bw.go b/products/unet/internal/eip/leave_shared_bw.go similarity index 100% rename from products/eip/internal/eip/leave_shared_bw.go rename to products/unet/internal/eip/leave_shared_bw.go diff --git a/products/eip/internal/eip/list.go b/products/unet/internal/eip/list.go similarity index 100% rename from products/eip/internal/eip/list.go rename to products/unet/internal/eip/list.go diff --git a/products/eip/internal/eip/modify_bw.go b/products/unet/internal/eip/modify_bw.go similarity index 100% rename from products/eip/internal/eip/modify_bw.go rename to products/unet/internal/eip/modify_bw.go diff --git a/products/eip/internal/eip/modify_traffic_mode.go b/products/unet/internal/eip/modify_traffic_mode.go similarity index 100% rename from products/eip/internal/eip/modify_traffic_mode.go rename to products/unet/internal/eip/modify_traffic_mode.go diff --git a/products/eip/internal/eip/release.go b/products/unet/internal/eip/release.go similarity index 100% rename from products/eip/internal/eip/release.go rename to products/unet/internal/eip/release.go diff --git a/products/eip/internal/eip/rows.go b/products/unet/internal/eip/rows.go similarity index 100% rename from products/eip/internal/eip/rows.go rename to products/unet/internal/eip/rows.go diff --git a/products/eip/internal/eip/status.go b/products/unet/internal/eip/status.go similarity index 100% rename from products/eip/internal/eip/status.go rename to products/unet/internal/eip/status.go diff --git a/products/eip/internal/eip/unbind.go b/products/unet/internal/eip/unbind.go similarity index 100% rename from products/eip/internal/eip/unbind.go rename to products/unet/internal/eip/unbind.go diff --git a/products/firewall/internal/firewall/add_rule.go b/products/unet/internal/firewall/add_rule.go similarity index 100% rename from products/firewall/internal/firewall/add_rule.go rename to products/unet/internal/firewall/add_rule.go diff --git a/products/firewall/internal/firewall/apply.go b/products/unet/internal/firewall/apply.go similarity index 100% rename from products/firewall/internal/firewall/apply.go rename to products/unet/internal/firewall/apply.go diff --git a/products/firewall/internal/firewall/cmd.go b/products/unet/internal/firewall/cmd.go similarity index 100% rename from products/firewall/internal/firewall/cmd.go rename to products/unet/internal/firewall/cmd.go diff --git a/products/firewall/internal/firewall/completion.go b/products/unet/internal/firewall/completion.go similarity index 100% rename from products/firewall/internal/firewall/completion.go rename to products/unet/internal/firewall/completion.go diff --git a/products/firewall/internal/firewall/copy.go b/products/unet/internal/firewall/copy.go similarity index 100% rename from products/firewall/internal/firewall/copy.go rename to products/unet/internal/firewall/copy.go diff --git a/products/firewall/internal/firewall/create.go b/products/unet/internal/firewall/create.go similarity index 100% rename from products/firewall/internal/firewall/create.go rename to products/unet/internal/firewall/create.go diff --git a/products/firewall/internal/firewall/delete.go b/products/unet/internal/firewall/delete.go similarity index 100% rename from products/firewall/internal/firewall/delete.go rename to products/unet/internal/firewall/delete.go diff --git a/products/firewall/internal/firewall/describe.go b/products/unet/internal/firewall/describe.go similarity index 100% rename from products/firewall/internal/firewall/describe.go rename to products/unet/internal/firewall/describe.go diff --git a/products/firewall/internal/firewall/list.go b/products/unet/internal/firewall/list.go similarity index 100% rename from products/firewall/internal/firewall/list.go rename to products/unet/internal/firewall/list.go diff --git a/products/firewall/internal/firewall/remove_rule.go b/products/unet/internal/firewall/remove_rule.go similarity index 100% rename from products/firewall/internal/firewall/remove_rule.go rename to products/unet/internal/firewall/remove_rule.go diff --git a/products/firewall/internal/firewall/resource.go b/products/unet/internal/firewall/resource.go similarity index 100% rename from products/firewall/internal/firewall/resource.go rename to products/unet/internal/firewall/resource.go diff --git a/products/firewall/internal/firewall/rows.go b/products/unet/internal/firewall/rows.go similarity index 100% rename from products/firewall/internal/firewall/rows.go rename to products/unet/internal/firewall/rows.go diff --git a/products/firewall/internal/firewall/rules.go b/products/unet/internal/firewall/rules.go similarity index 100% rename from products/firewall/internal/firewall/rules.go rename to products/unet/internal/firewall/rules.go diff --git a/products/firewall/internal/firewall/update.go b/products/unet/internal/firewall/update.go similarity index 100% rename from products/firewall/internal/firewall/update.go rename to products/unet/internal/firewall/update.go diff --git a/products/unet/product.go b/products/unet/product.go new file mode 100644 index 0000000000..8b07cf1aae --- /dev/null +++ b/products/unet/product.go @@ -0,0 +1,25 @@ +package unet + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" + internaleip "github.com/ucloud/ucloud-cli/products/unet/internal/eip" + internalfirewall "github.com/ucloud/ucloud-cli/products/unet/internal/firewall" +) + +type product struct{} + +// New returns the unet product (registered via hack/gen-products). +func New() cli.Product { return product{} } + +func (product) Metadata() cli.Metadata { + return cli.Metadata{Name: "unet", Commands: []string{"eip", "firewall"}} +} + +func (product) NewCommand(ctx *cli.Context) []*cobra.Command { + return []*cobra.Command{ + internaleip.NewCommand(ctx), + internalfirewall.NewCommand(ctx), + } +} diff --git a/products/unet/product.yaml b/products/unet/product.yaml new file mode 100644 index 0000000000..2398abcd51 --- /dev/null +++ b/products/unet/product.yaml @@ -0,0 +1,8 @@ +# products/unet/product.yaml — UNet 产品元数据(归属真源,owner 自治维护) +name: unet +owners: + - Episkey-G +commands: + - eip + - firewall +enabled: true diff --git a/products/firewall/testdata/cmdtree.golden b/products/unet/testdata/cmdtree.golden similarity index 50% rename from products/firewall/testdata/cmdtree.golden rename to products/unet/testdata/cmdtree.golden index 0bf9fd86dd..392f903068 100644 --- a/products/firewall/testdata/cmdtree.golden +++ b/products/unet/testdata/cmdtree.golden @@ -1,3 +1,60 @@ +ucloud eip use=eip short=List,allocate and release EIP +ucloud eip allocate use=allocate short=Allocate EIP + flag=bandwidth-mb short= default=0 required=true + flag=charge-type short= default=Month required= + flag=count short= default=1 required= + flag=group short= default=Default required= + flag=line short= default= required= + flag=name short= default=EIP required= + flag=project-id short= default= required= + flag=quantity short= default=1 required= + flag=region short= default= required= + flag=remark short= default= required= + flag=share-bandwidth-id short= default= required= + flag=traffic-mode short= default=Bandwidth required= +ucloud eip bind use=bind short=Bind EIP with uhost + flag=eip-id short= default=[] required=true + flag=project-id short= default= required= + flag=region short= default= required= + flag=resource-id short= default= required=true + flag=resource-type short= default=uhost required= +ucloud eip join-shared-bw use=join-shared-bw short=Join shared bandwidth + flag=eip-id short= default=[] required=true + flag=project-id short= default= required= + flag=region short= default= required= + flag=shared-bw-id short= default= required=true +ucloud eip leave-shared-bw use=leave-shared-bw short=Leave shared bandwidth + flag=bandwidth-mb short= default=1 required= + flag=eip-id short= default=[] required=true + flag=project-id short= default= required= + flag=region short= default= required= + flag=shared-bw-id short= default= required= + flag=traffic-mode short= default=Bandwidth required= +ucloud eip list use=list short=List all EIP instances + flag=limit short= default=50 required= + flag=list-all short= default=false required= + flag=offset short= default=0 required= + flag=page-off short= default=false required= + flag=project-id short= default= required= + flag=region short= default= required= +ucloud eip modify-bw use=modify-bw short=Modify bandwith of EIP instances + flag=bandwidth-mb short= default=0 required=true + flag=eip-id short= default=[] required=true + flag=project-id short= default= required= + flag=region short= default= required= +ucloud eip modify-traffic-mode use=modify-traffic-mode short=Modify charge mode of EIP instances + flag=eip-id short= default=[] required=true + flag=project-id short= default= required= + flag=region short= default= required= + flag=traffic-mode short= default= required=true +ucloud eip release use=release short=Release EIP + flag=eip-id short= default=[] required=true + flag=project-id short= default= required= + flag=region short= default= required= +ucloud eip unbind use=unbind short=Unbind EIP with uhost + flag=eip-id short= default=[] required=true + flag=project-id short= default= required= + flag=region short= default= required= ucloud firewall use=firewall short=List and manipulate extranet firewall ucloud firewall add-rule use=add-rule short=Add rule to firewall instance flag=fw-id short= default=[] required=true diff --git a/products/eip/testdata/completion.golden b/products/unet/testdata/completion.golden similarity index 68% rename from products/eip/testdata/completion.golden rename to products/unet/testdata/completion.golden index d2f9868b60..480488403d 100644 --- a/products/eip/testdata/completion.golden +++ b/products/unet/testdata/completion.golden @@ -22,3 +22,15 @@ ucloud eip release region dynamic ucloud eip unbind eip-id dynamic ucloud eip unbind project-id dynamic ucloud eip unbind region dynamic +ucloud firewall add-rule fw-id dynamic +ucloud firewall add-rule rules-file static +ucloud firewall apply fw-id dynamic +ucloud firewall apply resource-type static dbaudit,fortresshost,hadoophost,udhost,udockhost,uhost,unatgw,upm +ucloud firewall copy region dynamic +ucloud firewall copy src-fw dynamic +ucloud firewall copy target-region dynamic +ucloud firewall create rules-file static +ucloud firewall delete fw-id dynamic +ucloud firewall remove-rule fw-id dynamic +ucloud firewall resource fw-id dynamic +ucloud firewall update fw-id dynamic diff --git a/products/uphost/product.go b/products/uphost/product.go index a6e563e0ef..12f3d4c689 100644 --- a/products/uphost/product.go +++ b/products/uphost/product.go @@ -16,4 +16,6 @@ func (product) Metadata() cli.Metadata { return cli.Metadata{Name: "uphost", Commands: []string{"uphost"}} } -func (product) NewCommand(ctx *cli.Context) *cobra.Command { return internaluphost.NewCommand(ctx) } +func (product) NewCommand(ctx *cli.Context) []*cobra.Command { + return []*cobra.Command{internaluphost.NewCommand(ctx)} +}