Skip to content

Commit f243ad6

Browse files
hdbdn77caoxianfei1
authored andcommitted
docs: Quickly start developing curves based on cobra
Signed-off-by: Liao PengFei <[email protected]>
1 parent 9d83a9c commit f243ad6

File tree

2 files changed

+516
-0
lines changed

2 files changed

+516
-0
lines changed
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
# Curveadm CLI Development
2+
3+
## Cobra library
4+
5+
Curveadm CLI is developed based on the [Cobra](https://github.com/spf13/cobra) library, a Go language library for creating CLI command line programs.
6+
7+
### Basic use of Cobra
8+
9+
Create a root command using Cobra (print `root command` on the command line):
10+
```
11+
package main
12+
13+
import (
14+
"fmt"
15+
"os"
16+
"github.com/spf13/cobra"
17+
)
18+
19+
var rootCmd = &cobra.Command{
20+
Use: "hugo",
21+
Short: "Hugo is a very fast static site generator",
22+
Long: `A Fast and Flexible Static Site Generator built with
23+
love by spf13 and friends in Go.
24+
Complete documentation is available at https://gohugo.io/documentation/`,
25+
Run: func(cmd *cobra.Command, args []string) {
26+
fmt.Println("root command")
27+
},
28+
}
29+
30+
func Execute() {
31+
if err := rootCmd.Execute(); err != nil {
32+
fmt.Fprintln(os.Stderr, err)
33+
os.Exit(1)
34+
}
35+
}
36+
37+
func main() {
38+
rootCmd.Execute()
39+
}
40+
```
41+
- Use field sets the name of the command
42+
- Short field sets a short description
43+
- Long field setting detailed description
44+
- The Run field sets the function when executing the command
45+
46+
For more details on `Command` object fields and usage, see [Cobra library--command.go](https://github.com/spf13/cobra/blob/main/command.go).
47+
48+
### flag usage
49+
50+
Cobra supports parsing of custom parameters:
51+
```
52+
cmd.PersistentFlags().BoolP("help", "h", false, "Print usage")
53+
cmd.Flags().BoolVarP(&options.debug, "debug", "d", false, "Print debug information")
54+
```
55+
PersistentFlags() is used for global flags and can be used by the current command and its subcommands.
56+
57+
Flags() is used to define local flags, only for the current command.
58+
59+
For more details on the `flag` function and usage, see [Cobra library--command.go](https://github.com/spf13/cobra/blob/main/command.go) and [pflag library](https:// github.com/spf13/pflag).
60+
61+
### hook function
62+
63+
Cobra's Command object supports custom hook functions (PreRun and PostRun fields), and the hook function is run before and after the `run` command is executed. As follows:
64+
```
65+
cmd := &cobra.Command{
66+
Use: "root [sub]",
67+
Short: "My root command",
68+
PersistentPreRun: func(cmd *cobra.Command, args []string) {
69+
fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args)
70+
},
71+
PreRun: func(cmd *cobra.Command, args []string) {
72+
fmt.Printf("Inside rootCmd PreRun with args: %v\n", args)
73+
},
74+
Run: func(cmd *cobra.Command, args []string) {
75+
fmt.Printf("Inside rootCmd Run with args: %v\n", args)
76+
},
77+
PostRun: func(cmd *cobra.Command, args []string) {
78+
fmt.Printf("Inside rootCmd PostRun with args: %v\n", args)
79+
},
80+
PersistentPostRun: func(cmd *cobra.Command, args []string) {
81+
fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args)
82+
},
83+
}
84+
```
85+
The hook function will be executed in the order of (`PersistentPreRun`, `PreRun`, `Run`, `PostRun`, `PersistentPostRun`). Note: If the subcommand does not set `Persistent*Run`, it will automatically inherit the function definition of the parent command.
86+
87+
### Subcommands
88+
cobra allows nested commands, through the `AddCommand` function of the current command object. As follows:
89+
```
90+
rootCmd.AddCommand(versionCmd)
91+
```
92+
The recommended hierarchical command nesting structure is as follows:
93+
```
94+
├── cmd
95+
│ ├── root.go
96+
│ └── sub1
97+
│ ├── sub1.go
98+
│ └── sub2
99+
│ ├── leafA.go
100+
│ └── leafB.go
101+
└── main.go
102+
```
103+
Add the commands defined in leafA.go and leafB.go to the sub2 command.
104+
105+
Add the commands defined in sub2.go to the sub1 command.
106+
107+
Add the commands defined in sub1.go to the root command.
108+
109+
The main structure of the final command call is as follows:
110+
```
111+
root options
112+
root sub1 options
113+
root sub1 sub2 options
114+
root sub1 sub2 leafA options
115+
root sub1 sub2 leafB options
116+
```
117+
118+
119+
## curveadm cli project structure
120+
```
121+
cli
122+
├── cli
123+
│ ├── cli.go
124+
│ └── version.go
125+
├── command
126+
│  ├── audit.go
127+
│ ├── clean.go
128+
│ ├── client
129+
│ ├── cluster
130+
│ ...
131+
└── curveadm.go
132+
```
133+
The `cli.go` in the cli folder defines the `curveadm` object and related methods, which run through all curveadm cli command development.
134+
```
135+
type CurveAdm struct {
136+
rootDir string
137+
dataDir string
138+
...
139+
}
140+
func NewCurveAdm() (*CurveAdm, error) {
141+
curveadm := &CurveAdm{
142+
rootDir: rootDir,
143+
...
144+
}
145+
...
146+
return curveadm, nil
147+
}
148+
```
149+
150+
The command directory stores command implementations at each level.
151+
```
152+
├── audit.go
153+
├── client
154+
│ ├── cmd.go
155+
│ ├── enter.go
156+
│ └── unmap.go
157+
├── cluster
158+
│ ├── add.go
159+
│ ├── cmd.go
160+
├── cmd.go
161+
├── deploy.go
162+
```
163+
In curveadm cli, the root command of each layer is defined in `cmd.go`. The root command is only responsible for registering subcommands and providing help information, and does not participate in actual work operations.
164+
```
165+
cli\command\cmd.go
166+
167+
func addSubCommands(cmd *cobra.Command, curveadm *cli.CurveAdm) {
168+
cmd.AddCommand(
169+
client.NewClientCommand(curveadm), // curveadm client
170+
...
171+
)
172+
}
173+
func NewCurveAdmCommand(curveadm *cli.CurveAdm) *cobra.Command {
174+
...
175+
cmd := &cobra.Command{
176+
Use: "curveadm [OPTIONS] COMMAND [ARGS...]",
177+
...
178+
}
179+
...
180+
addSubCommands(cmd, curveadm)
181+
return cmd
182+
}
183+
################################################ ##############
184+
cli\command\client\cmd.go
185+
186+
func NewClientCommand(curveadm *cli.CurveAdm) *cobra.Command {
187+
cmd := &cobra.Command{
188+
Use: "client",
189+
...
190+
}
191+
cmd.AddCommand(
192+
NewMapCommand(curveadm),
193+
...
194+
)
195+
...
196+
}
197+
################################################ ##############
198+
cli\command\client\enter.go
199+
200+
func NewEnterCommand(curveadm *cli.CurveAdm) *cobra.Command {
201+
...
202+
cmd := &cobra.Command{
203+
Use: "enter ID",
204+
...
205+
}
206+
...
207+
}
208+
```
209+
The final call structure of the enter command is as follows:
210+
```
211+
curveadm client enter ID
212+
```
213+
214+
curveadm.go defines the execution function of the `curveadm` root command and performs related audit work.
215+
```
216+
func Execute() {
217+
curveadm, err := cli.NewCurveAdm()
218+
...
219+
id := curveadm.PreAudit(time.Now(), os.Args[1:])
220+
cmd := command.NewCurveAdmCommand(curveadm)
221+
err = cmd.Execute()
222+
curveadm.PostAudit(id, err)
223+
if err != nil {
224+
os.Exit(1)
225+
}
226+
}
227+
```
228+
229+
The entrance to the `curveadm` main program is under the [curveadm folder](https://github.com/opencurve/curveadm/tree/develop/cmd/curveadm). You can execute the operation and execution of `curveadm` in this directory. compile
230+
```
231+
func main() {
232+
cli.Execute()
233+
}
234+
```
235+
236+
### curveadm general tools
237+
For command development of curveadm cli, curveadm provides general tools, such as:
238+
239+
- cliutil.NoArgs: used to determine whether the command does not contain parameters
240+
241+
- cliutil.ShowHelp: used to display help information when the command is run
242+
243+
In the [curveadm/internal directory](https://github.com/opencurve/curveadm/tree/develop/internal). As follows:
244+
```
245+
import (
246+
cliutil "github.com/opencurve/curveadm/internal/utils"
247+
...
248+
)
249+
250+
cmd := &cobra.Command{
251+
Use: "client",
252+
Args: cliutil.NoArgs,
253+
RunE: cliutil.ShowHelp(curveadm.Err()),
254+
}
255+
```
256+
`cliutil.NoArgs` specifies that the `curveadm client` command does not contain any arguments (except subcommands); the `cliutil.ShowHelp` function displays the defined help options when running the `curveadm client` command directly.
257+
258+
For more common commands and usage, please refer to [internal folder](https://github.com/opencurve/curveadm/tree/develop/internal).

0 commit comments

Comments
 (0)