diff --git a/bapps/app.go b/bapps/app.go index deaa0c8..2f8e0b5 100644 --- a/bapps/app.go +++ b/bapps/app.go @@ -3,12 +3,12 @@ package bapps import ( "log" - "github.com/milvus-io/birdwatcher/states" + "github.com/milvus-io/birdwatcher/framework" ) // BApp interface for birdwatcher application type BApp interface { - Run(states.State) + Run(framework.State) } // AppOption application setup option function. diff --git a/bapps/go_prompt.go b/bapps/go_prompt.go index 9245f3f..057ee27 100644 --- a/bapps/go_prompt.go +++ b/bapps/go_prompt.go @@ -11,19 +11,20 @@ import ( "github.com/c-bata/go-prompt" "github.com/milvus-io/birdwatcher/configs" + "github.com/milvus-io/birdwatcher/framework" "github.com/milvus-io/birdwatcher/history" - "github.com/milvus-io/birdwatcher/states" "github.com/samber/lo" ) // PromptApp wraps go-prompt as application. type PromptApp struct { exited bool - currentState states.State + currentState framework.State sugguestHistory bool historyHelper *history.Helper logger *log.Logger prompt *prompt.Prompt + config *configs.Config } func NewPromptApp(config *configs.Config, opts ...AppOption) BApp { @@ -32,9 +33,14 @@ func NewPromptApp(config *configs.Config, opts ...AppOption) BApp { o(opt) } + config.Logger = opt.logger + // use workspace path to open&store history log hh := history.NewHistoryHelper(config.WorkspacePath) - pa := &PromptApp{historyHelper: hh} + pa := &PromptApp{ + historyHelper: hh, + config: config, + } pa.logger = opt.logger historyItems := hh.List("") @@ -69,7 +75,7 @@ func NewPromptApp(config *configs.Config, opts ...AppOption) BApp { return pa } -func (a *PromptApp) Run(start states.State) { +func (a *PromptApp) Run(start framework.State) { a.currentState = start a.prompt.Run() } @@ -132,7 +138,7 @@ func (a *PromptApp) promptExecute(in string) { os.Stdout = stdout close(pagerSig) } - a.currentState, _ = a.currentState.Process(in) + nextState, err := a.currentState.Process(in) if writer != nil { writer.Close() } @@ -143,6 +149,14 @@ func (a *PromptApp) promptExecute(in string) { a.historyHelper.AddLog(in) a.sugguestHistory = false + if err != nil { + fmt.Println(err.Error()) + return + } + + nextState.SetupCommands() + a.currentState = nextState + if a.currentState.IsEnding() { fmt.Println("Bye!") a.exited = true diff --git a/bapps/olc.go b/bapps/olc.go index db4a948..1823e25 100644 --- a/bapps/olc.go +++ b/bapps/olc.go @@ -5,7 +5,7 @@ import ( "os" "strings" - "github.com/milvus-io/birdwatcher/states" + "github.com/milvus-io/birdwatcher/framework" "github.com/samber/lo" ) @@ -24,7 +24,7 @@ func NewOlcApp(script string) BApp { } } -func (a *olcApp) Run(start states.State) { +func (a *olcApp) Run(start framework.State) { app := start cmds := a.parseScripts(a.script) var err error diff --git a/bapps/promptui.go b/bapps/promptui.go index 4885218..91a0133 100644 --- a/bapps/promptui.go +++ b/bapps/promptui.go @@ -4,12 +4,13 @@ import ( "errors" "github.com/manifoldco/promptui" - "github.com/milvus-io/birdwatcher/states" + "github.com/milvus-io/birdwatcher/common" + "github.com/milvus-io/birdwatcher/framework" ) // simpleApp wraps promptui as BApp. type simpleApp struct { - currentState states.State + currentState framework.State } func NewSimpleApp() BApp { @@ -17,7 +18,7 @@ func NewSimpleApp() BApp { } // Run starts BirdWatcher with promptui. (disable suggestion and history) -func (a *simpleApp) Run(start states.State) { +func (a *simpleApp) Run(start framework.State) { app := start for { p := promptui.Prompt{ @@ -30,12 +31,13 @@ func (a *simpleApp) Run(start states.State) { line, err := p.Run() if err == nil { app, err = app.Process(line) - if errors.Is(err, states.ExitErr) { + if errors.Is(err, common.ExitErr) { break } if app.IsEnding() { return } + app.SetupCommands() } } } diff --git a/bapps/webserver.go b/bapps/webserver.go index c5adb33..59dbcbb 100644 --- a/bapps/webserver.go +++ b/bapps/webserver.go @@ -28,7 +28,7 @@ type InstanceInfo struct { RootPath string `form:"rootPath"` } -func (app *WebServerApp) Run(states.State) { +func (app *WebServerApp) Run(framework.State) { r := gin.Default() etcdversion.SetVersion(models.GTEVersion2_2) @@ -41,7 +41,7 @@ func (app *WebServerApp) Run(states.State) { r.Run(fmt.Sprintf(":%d", app.port)) } -func (app *WebServerApp) ParseRouter(r *gin.Engine, s states.State) { +func (app *WebServerApp) ParseRouter(r *gin.Engine, s framework.State) { v := reflect.ValueOf(s) tp := v.Type() @@ -105,7 +105,7 @@ func (app *WebServerApp) parseMethod(r *gin.Engine, mt reflect.Method, name stri //fmt.Println(mt.Name) cp := reflect.New(paramType.Elem()).Interface().(framework.CmdParam) - fUse, _ := states.GetCmdFromFlag(cp) + fUse, _ := framework.GetCmdFromFlag(cp) if len(use) == 0 { use = fUse } @@ -114,7 +114,7 @@ func (app *WebServerApp) parseMethod(r *gin.Engine, mt reflect.Method, name stri fnName := mt.Name use = strings.ToLower(fnName[:len(fnName)-8]) } - uses := states.ParseUseSegments(use) + uses := framework.ParseUseSegments(use) lastKw := uses[len(uses)-1] // hard code, show xxx command only if uses[0] != "show" { @@ -135,6 +135,7 @@ func (app *WebServerApp) parseMethod(r *gin.Engine, mt reflect.Method, name stri c.Error(err) return } + s.SetupCommands() v := reflect.ValueOf(s) cp := reflect.New(paramType.Elem()).Interface().(framework.CmdParam) diff --git a/common/errors.go b/common/errors.go new file mode 100644 index 0000000..b358049 --- /dev/null +++ b/common/errors.go @@ -0,0 +1,12 @@ +package common + +// ExitErr is the error indicates user needs to exit application. +var ExitErr = exitErr{} + +// exitErr internal err type for comparing. +type exitErr struct{} + +// Error implements error. +func (e exitErr) Error() string { + return "exited" +} diff --git a/common/version.go b/common/version.go index 396affb..b67a3db 100644 --- a/common/version.go +++ b/common/version.go @@ -6,5 +6,5 @@ import "github.com/blang/semver/v4" var Version semver.Version func init() { - Version = semver.MustParse("1.0.2") + Version = semver.MustParse("1.1.0-dev") } diff --git a/configs/config.go b/configs/config.go index b137f74..90b68a9 100644 --- a/configs/config.go +++ b/configs/config.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io/ioutil" + "log" "os" "path" @@ -27,6 +28,8 @@ type Config struct { ConfigPath string `yaml:"-"` // backup workspace path, default $PWD/bw_workspace WorkspacePath string `yaml:"WorkspacePath"` + + Logger *log.Logger } func (c *Config) load() error { diff --git a/states/states.go b/framework/command.go similarity index 57% rename from states/states.go rename to framework/command.go index b32a487..48fda90 100644 --- a/states/states.go +++ b/framework/command.go @@ -1,145 +1,17 @@ -package states +package framework import ( "context" "errors" "fmt" - "os" - "os/signal" "reflect" "strconv" "strings" - "syscall" - "github.com/milvus-io/birdwatcher/framework" - "github.com/milvus-io/birdwatcher/states/autocomplete" "github.com/spf13/cobra" "github.com/spf13/pflag" ) -// State is the interface for application state. -type State interface { - Ctx() (context.Context, context.CancelFunc) - Label() string - Process(cmd string) (State, error) - Close() - SetNext(state State) - Suggestions(input string) map[string]string - SetupCommands() - IsEnding() bool -} - -// cmdState is the basic state to process input command. -type cmdState struct { - label string - rootCmd *cobra.Command - nextState State - signal <-chan os.Signal - - setupFn func() -} - -// Ctx returns context which bind to sigint handler. -func (s *cmdState) Ctx() (context.Context, context.CancelFunc) { - ctx, cancel := context.WithCancel(context.Background()) - go func() { - defer cancel() - select { - case <-s.signal: - case <-ctx.Done(): - } - }() - return ctx, cancel -} - -// SetupCommands perform command setup & reset. -func (s *cmdState) SetupCommands() { - if s.setupFn != nil { - s.setupFn() - } -} - -// mergeFunctionCommands parses all member methods for provided state and add it into cmd. -func (s *cmdState) mergeFunctionCommands(cmd *cobra.Command, state State) { - items := parseFunctionCommands(state) - for _, item := range items { - target := cmd - for _, kw := range item.kws { - node, _, err := cmd.Find([]string{kw}) - if err != nil { - newNode := &cobra.Command{Use: kw} - target.AddCommand(newNode) - node = newNode - } - target = node - } - target.AddCommand(item.cmd) - } -} - -// Label returns the display label for current cli. -func (s *cmdState) Label() string { - return s.label -} - -func (s *cmdState) Suggestions(input string) map[string]string { - return autocomplete.SuggestInputCommands(input, s.rootCmd.Commands()) -} - -// Process is the main entry for processing command. -func (s *cmdState) Process(cmd string) (State, error) { - args := strings.Split(cmd, " ") - - target, _, err := s.rootCmd.Find(args) - if err == nil && target != nil { - defer target.SetArgs(nil) - } - - signal.Reset(syscall.SIGINT) - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT) - s.signal = c - - s.rootCmd.SetArgs(args) - err = s.rootCmd.Execute() - signal.Reset(syscall.SIGINT) - if errors.Is(err, ExitErr) { - return s.nextState, ExitErr - } - if err != nil { - return s, err - } - if s.nextState != nil { - nextState := s.nextState - s.nextState = nil - return nextState, nil - } - - // reset command states - s.SetupCommands() - return s, nil -} - -// SetNext simple method to set next state. -func (s *cmdState) SetNext(state State) { - s.nextState = state -} - -// Close empty method to implement State. -func (s *cmdState) Close() {} - -// Check state is ending state. -func (s *cmdState) IsEnding() bool { return false } - -type exitParam struct { - framework.ParamBase `use:"exit" desc:"Close this CLI tool"` -} - -// ExitCommand returns exit command -func (s *cmdState) ExitCommand(ctx context.Context, _ *exitParam) { - s.SetNext(&exitState{}) -} - type commandItem struct { kws []string cmd *cobra.Command @@ -193,10 +65,10 @@ func parseMethod(state State, mt reflect.Method) (*cobra.Command, []string, bool if t.NumIn() > 2 { // should be CmdParam in := t.In(2) - if !in.Implements(reflect.TypeOf((*framework.CmdParam)(nil)).Elem()) { + if !in.Implements(reflect.TypeOf((*CmdParam)(nil)).Elem()) { return nil, nil, false } - cp, ok := reflect.New(in.Elem()).Interface().(framework.CmdParam) + cp, ok := reflect.New(in.Elem()).Interface().(CmdParam) if !ok { fmt.Println("conversion failed", in.Name()) } else { @@ -205,8 +77,7 @@ func parseMethod(state State, mt reflect.Method) (*cobra.Command, []string, bool } } - //fmt.Println(mt.Name) - cp := reflect.New(paramType.Elem()).Interface().(framework.CmdParam) + cp := reflect.New(paramType.Elem()).Interface().(CmdParam) fUse, fDesc := GetCmdFromFlag(cp) if len(use) == 0 { use = fUse @@ -227,7 +98,7 @@ func parseMethod(state State, mt reflect.Method) (*cobra.Command, []string, bool setupFlags(cp, cmd.Flags()) cmd.Short = short cmd.Run = func(cmd *cobra.Command, args []string) { - cp := reflect.New(paramType.Elem()).Interface().(framework.CmdParam) + cp := reflect.New(paramType.Elem()).Interface().(CmdParam) cp.ParseArgs(args) if err := parseFlags(cp, cmd.Flags()); err != nil { @@ -254,23 +125,23 @@ func parseMethod(state State, mt reflect.Method) (*cobra.Command, []string, bool err := result.Interface().(error) fmt.Println(err.Error()) return - case result.Type().Implements(reflect.TypeOf((*framework.ResultSet)(nil)).Elem()): + case result.Type().Implements(reflect.TypeOf((*ResultSet)(nil)).Elem()): if result.IsNil() { continue } - rs := result.Interface().(framework.ResultSet) - if preset, ok := rs.(*framework.PresetResultSet); ok { + rs := result.Interface().(ResultSet) + if preset, ok := rs.(*PresetResultSet); ok { fmt.Println(preset.String()) return } - fmt.Println(rs.PrintAs(framework.FormatDefault)) + fmt.Println(rs.PrintAs(FormatDefault)) } } } return cmd, uses, true } -func GetCmdFromFlag(p framework.CmdParam) (string, string) { +func GetCmdFromFlag(p CmdParam) (string, string) { v := reflect.ValueOf(p) if v.Kind() != reflect.Pointer { fmt.Println("param is not pointer") @@ -315,7 +186,8 @@ func ParseUseSegments(use string) []string { return result } -func setupFlags(p framework.CmdParam, flags *pflag.FlagSet) { +// setupFlags performs command flag setup with CmdParam provided information. +func setupFlags(p CmdParam, flags *pflag.FlagSet) { v := reflect.ValueOf(p) if v.Kind() != reflect.Pointer { fmt.Println("param is not pointer") @@ -358,8 +230,8 @@ func setupFlags(p framework.CmdParam, flags *pflag.FlagSet) { } } -func parseFlags(p framework.CmdParam, flags *pflag.FlagSet) error { - +// parseFlags parse parameters from flagset and setup value via reflection. +func parseFlags(p CmdParam, flags *pflag.FlagSet) error { v := reflect.ValueOf(p) if v.Kind() != reflect.Pointer { return errors.New("param is not pointer") diff --git a/framework/state.go b/framework/state.go new file mode 100644 index 0000000..e5d1708 --- /dev/null +++ b/framework/state.go @@ -0,0 +1,175 @@ +package framework + +import ( + "context" + "errors" + "os" + "os/signal" + "strings" + "syscall" + + "github.com/milvus-io/birdwatcher/common" + "github.com/milvus-io/birdwatcher/states/autocomplete" + "github.com/spf13/cobra" +) + +// State is the interface for application state. +type State interface { + Ctx() (context.Context, context.CancelFunc) + Label() string + Process(cmd string) (State, error) + CanProcess(cmd string) bool + Close() + SetNext(state State) + Suggestions(input string) map[string]string + SetupCommands() + IsEnding() bool +} + +// SetupFunc function type for setup commands. +type SetupFunc func() + +// CmdState wraps cobra command as State interface. +type CmdState struct { + parent *CmdState + label string + RootCmd *cobra.Command + nextState State + signal <-chan os.Signal + + SetupFn func() +} + +// NewCmdState returns a CmdState with provided label. +func NewCmdState(label string) *CmdState { + return &CmdState{ + label: label, + } +} + +// SetLabel updates label value. +func (s *CmdState) SetLabel(label string) { + s.label = label +} + +// Spawn returns a child command connected to current state as parent. +func (s *CmdState) Spawn(label string) *CmdState { + return &CmdState{ + parent: s, + label: label, + } +} + +// GetCmd returns the command instance for SetupCommands(). +// if parent presents and root cmd not nil, use parent command. +// otherwise, create a new cobra.Command. +func (s *CmdState) GetCmd() *cobra.Command { + if s.parent != nil && s.parent.RootCmd != nil { + return s.parent.RootCmd + } + + return &cobra.Command{} +} + +func (s *CmdState) UpdateState(cmd *cobra.Command, state State, fn SetupFunc) { + s.MergeFunctionCommands(cmd, state) + s.RootCmd = cmd + s.SetupFn = fn +} + +// Ctx returns context which bind to sigint handler. +func (s *CmdState) Ctx() (context.Context, context.CancelFunc) { + ctx, cancel := context.WithCancel(context.Background()) + go func() { + defer cancel() + select { + case <-s.signal: + case <-ctx.Done(): + } + }() + return ctx, cancel +} + +// SetupCommands perform command setup & reset. +func (s *CmdState) SetupCommands() { + if s.SetupFn != nil { + s.SetupFn() + } +} + +// mergeFunctionCommands parses all member methods for provided state and add it into cmd. +func (s *CmdState) MergeFunctionCommands(cmd *cobra.Command, state State) { + items := parseFunctionCommands(state) + for _, item := range items { + target := cmd + for _, kw := range item.kws { + node, _, err := target.Find([]string{kw}) + if err != nil || (node != nil && node.Use == "") { + newNode := &cobra.Command{Use: kw} + target.AddCommand(newNode) + node = newNode + } + target = node + } + target.AddCommand(item.cmd) + } +} + +// Label returns the display label for current cli. +func (s *CmdState) Label() string { + return s.label +} + +func (s *CmdState) Suggestions(input string) map[string]string { + return autocomplete.SuggestInputCommands(input, s.RootCmd.Commands()) +} + +// Process is the main entry for processing command. +func (s *CmdState) Process(cmd string) (State, error) { + args := strings.Split(cmd, " ") + + target, _, err := s.RootCmd.Find(args) + if err == nil && target != nil { + defer target.SetArgs(nil) + } + + signal.Reset(syscall.SIGINT) + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT) + s.signal = c + + s.RootCmd.SetArgs(args) + err = s.RootCmd.Execute() + signal.Reset(syscall.SIGINT) + + if errors.Is(err, common.ExitErr) { + return s.nextState, common.ExitErr + } + if err != nil { + return s, err + } + if s.nextState != nil { + nextState := s.nextState + s.nextState = nil + return nextState, nil + } + + return s, nil +} + +func (s *CmdState) CanProcess(cmd string) bool { + args := strings.Split(cmd, " ") + target, _, err := s.RootCmd.Find(args) + return target != nil && err == nil +} + +// SetNext simple method to set next state. +func (s *CmdState) SetNext(state State) { + s.nextState = state +} + +// Close empty method to implement State. +func (s *CmdState) Close() {} + +// Check state is ending state. +func (s *CmdState) IsEnding() bool { return false } diff --git a/states/app_control.go b/states/app_control.go new file mode 100644 index 0000000..cde223a --- /dev/null +++ b/states/app_control.go @@ -0,0 +1,85 @@ +package states + +import ( + "context" + "fmt" + "sort" + + "github.com/cockroachdb/errors" + "github.com/milvus-io/birdwatcher/framework" + "github.com/samber/lo" + "github.com/spf13/cobra" +) + +// getExitCmd returns exit command for input state. +func getExitCmd(state framework.State) *cobra.Command { + cmd := &cobra.Command{ + Use: "exit", + Short: "Closes the cli", + Aliases: []string{"quit"}, + RunE: func(*cobra.Command, []string) error { + state.SetNext(&exitState{}) + // cannot return ExitErr here to avoid print help message + return nil + }, + } + return cmd +} + +// exitState simple exit state. +type exitState struct { + framework.CmdState +} + +// SetupCommands setups the command. +// also called after each command run to reset flag values. +func (s *exitState) SetupCommands() {} + +// IsEnding returns true for exit State +func (s *exitState) IsEnding() bool { return true } + +func (app *ApplicationState) listStates() []string { + statesNames := lo.Keys(app.states) + sort.Strings(statesNames) + return statesNames +} + +type ListStatesParam struct { + framework.ParamBase `use:"list states" desc:"list current connected states"` +} + +func (app *ApplicationState) ListStatesCommand(ctx context.Context, p *ListStatesParam) error { + statesNames := app.listStates() + for _, stateName := range statesNames { + fmt.Printf("%s\t%s\n", stateName, app.states[stateName].Label()) + } + return nil +} + +type DisconnectParam struct { + framework.ParamBase `use:"disconnect" desc:"disconnect online states"` + components []string +} + +func (p *DisconnectParam) ParseArgs(args []string) error { + if len(args) == 0 { + return errors.New("disconnect component not provided") + } + p.components = args + return nil +} + +// DisconnectCommand implements disconnect sub state logic. +func (app *ApplicationState) DisconnectCommand(ctx context.Context, p *DisconnectParam) error { + for _, comp := range p.components { + state, ok := app.states[comp] + if !ok { + fmt.Printf("State %s not connected.\n", comp) + continue + } + state.Close() + delete(app.states, comp) + fmt.Printf("%s State disconnected.\n", comp) + } + return nil +} diff --git a/states/app_state.go b/states/app_state.go new file mode 100644 index 0000000..b413cf8 --- /dev/null +++ b/states/app_state.go @@ -0,0 +1,128 @@ +package states + +import ( + "context" + "strings" + + "github.com/milvus-io/birdwatcher/configs" + "github.com/milvus-io/birdwatcher/framework" + "github.com/milvus-io/birdwatcher/states/storage" + "github.com/samber/lo" + "github.com/spf13/cobra" +) + +// ApplicationState application background state. +// used for state switch/merging. +type ApplicationState struct { + // current state + states map[string]framework.State + + root *cobra.Command + core *framework.CmdState + + // config stores configuration items + config *configs.Config +} + +func (app *ApplicationState) Ctx() (context.Context, context.CancelFunc) { + return app.core.Ctx() +} + +func (app *ApplicationState) Label() string { + if len(app.states) == 0 { + return "Offline" + } + builder := &strings.Builder{} + + for _, name := range app.listStates() { + builder.WriteString(app.states[name].Label()) + } + return builder.String() +} + +func (app *ApplicationState) Process(cmd string) (framework.State, error) { + for key, state := range app.states { + if !state.CanProcess(cmd) { + continue + } + next, err := state.Process(cmd) + if err != nil { + return nil, err + } + + app.states[key] = next + return app, nil + } + app.core.Process(cmd) + + return app, nil +} + +func (app *ApplicationState) CanProcess(cmd string) bool { + return true +} + +func (app *ApplicationState) Close() { + for _, state := range app.states { + state.Close() + } +} + +func (app *ApplicationState) SetNext(state framework.State) { + app.config.Logger.Println("SetNext called for ApplicationState, which is not expected.") +} + +func (app *ApplicationState) SetTagNext(tag string, state framework.State) { + app.states[tag] = state +} + +func (app *ApplicationState) Suggestions(input string) map[string]string { + result := make(map[string]string) + states := append(lo.MapToSlice(app.states, func(_ string, state framework.State) framework.State { + return state + }), app.core) + for _, state := range states { + for k, v := range state.Suggestions(input) { + result[k] = v + } + } + return result +} + +// SetupCommands implments framework.State. +// initialize or reset command after execution. +func (app *ApplicationState) SetupCommands() { + cmd := app.core.GetCmd() + app.core.UpdateState(cmd, app, app.SetupCommands) + + for _, state := range app.states { + state.SetupCommands() + } +} + +func (app *ApplicationState) IsEnding() bool { + for _, state := range app.states { + if state.IsEnding() { + return true + } + } + return false +} + +func (app *ApplicationState) ConnectMinioCommand(ctx context.Context, p *storage.ConnectMinioParam) error { + state, err := storage.ConnectMinio(ctx, p) + if err != nil { + return err + } + + app.SetTagNext(minioTag, state) + return nil +} + +type exitParam struct { + framework.ParamBase `use:"exit" desc:"Close this CLI tool"` +} + +func (app *ApplicationState) ExitCommand(ctx context.Context, _ *exitParam) { + app.SetTagNext("exit", &exitState{}) +} diff --git a/states/autocomplete/auto_complete.go b/states/autocomplete/auto_complete.go index 04c23ae..5479f9b 100644 --- a/states/autocomplete/auto_complete.go +++ b/states/autocomplete/auto_complete.go @@ -2,7 +2,7 @@ package autocomplete import ( "fmt" - "os" + "io/fs" "strings" "github.com/spf13/cobra" @@ -41,7 +41,7 @@ func (c *cmdCandidate) args() []acCandidate { result = append(result, &fileCandidate{previousCandidates: []acCandidate{}}) } if name == "directory" { - result = append(result, &fileCandidate{previousCandidates: []acCandidate{}, validator: func(info os.FileInfo) bool { return info.IsDir() }}) + result = append(result, &fileCandidate{previousCandidates: []acCandidate{}, validator: func(info fs.DirEntry) bool { return info.IsDir() }}) } } } diff --git a/states/autocomplete/auto_complete_file.go b/states/autocomplete/auto_complete_file.go index 804c36a..c5d3b73 100644 --- a/states/autocomplete/auto_complete_file.go +++ b/states/autocomplete/auto_complete_file.go @@ -2,7 +2,7 @@ package autocomplete import ( "fmt" - "io/ioutil" + "io/fs" "os" "path" "strings" @@ -12,7 +12,7 @@ import ( type fileCandidate struct { previousCandidates []acCandidate - validator func(file os.FileInfo) bool + validator func(fs.DirEntry) bool //func(file os.FileInfo) bool } func (c *fileCandidate) Match(input cComp) bool { @@ -56,7 +56,7 @@ func (c *fileCandidate) Suggest(target cComp) map[string]string { return map[string]string{} } - fs, err := ioutil.ReadDir(d) + fs, err := os.ReadDir(d) if err != nil { return map[string]string{} } diff --git a/states/backup_mock_connect.go b/states/backup_mock_connect.go index 7f2653e..ed2e165 100644 --- a/states/backup_mock_connect.go +++ b/states/backup_mock_connect.go @@ -13,6 +13,7 @@ import ( "github.com/cockroachdb/errors" "github.com/golang/protobuf/proto" "github.com/milvus-io/birdwatcher/configs" + "github.com/milvus-io/birdwatcher/framework" "github.com/milvus-io/birdwatcher/models" "github.com/milvus-io/birdwatcher/states/etcd" "github.com/milvus-io/birdwatcher/states/etcd/remove" @@ -28,7 +29,7 @@ const ( ) type embedEtcdMockState struct { - cmdState + *framework.CmdState *show.ComponentShow *remove.ComponentRemove client *clientv3.Client @@ -41,7 +42,7 @@ type embedEtcdMockState struct { config *configs.Config } -// Close implements State. +// Close implements framework.State. // Clean up embed etcd folder content. func (s *embedEtcdMockState) Close() { if s.client != nil { @@ -84,19 +85,18 @@ func (s *embedEtcdMockState) SetupCommands() { ) cmd.AddCommand(etcd.RawCommands(s.client)...) - s.mergeFunctionCommands(cmd, s) + s.MergeFunctionCommands(cmd, s) - s.cmdState.rootCmd = cmd - s.setupFn = s.SetupCommands + s.CmdState.RootCmd = cmd + s.SetupFn = s.SetupCommands } func (s *embedEtcdMockState) SetInstance(instanceName string) { - s.cmdState.label = fmt.Sprintf("Backup(%s)", instanceName) + s.SetLabel(fmt.Sprintf("Backup(%s)", instanceName)) s.instanceName = instanceName rootPath := path.Join(instanceName, metaPath) s.ComponentShow = show.NewComponent(s.client, s.config, rootPath) s.ComponentRemove = remove.NewComponent(s.client, s.config, rootPath) - s.SetupCommands() } func (s *embedEtcdMockState) setupWorkDir(dir string) error { @@ -173,14 +173,12 @@ func (s *embedEtcdMockState) readWorkspaceMeta(path string) { s.SetInstance(meta.Instance) } -func getEmbedEtcdInstance(server *embed.Etcd, cli *clientv3.Client, instanceName string, config *configs.Config) State { +func getEmbedEtcdInstance(server *embed.Etcd, cli *clientv3.Client, instanceName string, config *configs.Config) framework.State { basePath := path.Join(instanceName, metaPath) state := &embedEtcdMockState{ - cmdState: cmdState{ - label: fmt.Sprintf("Backup(%s)", instanceName), - }, + CmdState: framework.NewCmdState(fmt.Sprintf("Backup(%s)", instanceName)), ComponentShow: show.NewComponent(cli, config, basePath), ComponentRemove: remove.NewComponent(cli, config, basePath), instanceName: instanceName, @@ -196,10 +194,10 @@ func getEmbedEtcdInstance(server *embed.Etcd, cli *clientv3.Client, instanceName return state } -func getEmbedEtcdInstanceV2(server *embed.Etcd, config *configs.Config) *embedEtcdMockState { +func getEmbedEtcdInstanceV2(parent *framework.CmdState, server *embed.Etcd, config *configs.Config) *embedEtcdMockState { client := v3client.New(server.Server) state := &embedEtcdMockState{ - cmdState: cmdState{}, + CmdState: parent.Spawn(""), server: server, client: client, metrics: make(map[string][]byte), diff --git a/states/disconnected.go b/states/disconnected.go deleted file mode 100644 index 61324c9..0000000 --- a/states/disconnected.go +++ /dev/null @@ -1,32 +0,0 @@ -package states - -import ( - "github.com/milvus-io/birdwatcher/configs" - "github.com/spf13/cobra" -) - -type disconnectState struct { - cmdState - config *configs.Config -} - -func (s *disconnectState) SetupCommands() { - cmd := &cobra.Command{} - - s.mergeFunctionCommands(cmd, s) - - s.rootCmd = cmd - s.setupFn = s.SetupCommands -} - -func getDisconnectedState(config *configs.Config) State { - state := &disconnectState{ - cmdState: cmdState{ - label: "Offline", - }, - config: config, - } - - state.SetupCommands() - return state -} diff --git a/states/etcd/show/collection.go b/states/etcd/show/collection.go index 8e185ef..29f493c 100644 --- a/states/etcd/show/collection.go +++ b/states/etcd/show/collection.go @@ -42,7 +42,7 @@ func (c *ComponentShow) CollectionCommand(ctx context.Context, p *CollectionPara if p.DatabaseID > -1 && coll.DBID != p.DatabaseID { return false } - if p.State != "" && strings.ToLower(p.State) != strings.ToLower(coll.State.String()) { + if p.State != "" && strings.EqualFold(p.State, coll.State.String()) { return false } total++ diff --git a/states/etcd_connect.go b/states/etcd_connect.go index c4d90e3..2f50a69 100644 --- a/states/etcd_connect.go +++ b/states/etcd_connect.go @@ -10,7 +10,6 @@ import ( "github.com/milvus-io/birdwatcher/configs" "github.com/milvus-io/birdwatcher/framework" - "github.com/spf13/cobra" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" ) @@ -37,16 +36,7 @@ func pingEtcd(ctx context.Context, cli clientv3.KV, rootPath string, metaPath st return nil } -type ConnectParams struct { - framework.ParamBase `use:"connect" desc:"Connect to etcd"` - EtcdAddr string `name:"etcd" default:"127.0.0.1:2379" desc:"the etcd endpoint to connect"` - RootPath string `name:"rootPath" default:"by-dev" desc:"meta root paht milvus is using"` - MetaPath string `name:"metaPath" default:"meta" desc:"meta path prefix"` - Force bool `name:"force" default:"false" desc:"force connect ignoring ping Etcd & rootPath check"` - Dry bool `name:"dry" default:"false" desc:"dry connect without specifying milvus instance"` -} - -func (s *disconnectState) ConnectCommand(ctx context.Context, cp *ConnectParams) error { +func (app *ApplicationState) ConnectCommand(ctx context.Context, cp *ConnectParams) error { etcdCli, err := clientv3.New(clientv3.Config{ Endpoints: []string{cp.EtcdAddr}, DialTimeout: time.Second * 10, @@ -59,7 +49,7 @@ func (s *disconnectState) ConnectCommand(ctx context.Context, cp *ConnectParams) return err } - etcdState := getEtcdConnectedState(etcdCli, cp.EtcdAddr, s.config) + etcdState := getEtcdConnectedState(app.core, etcdCli, cp.EtcdAddr, app.config) if !cp.Dry { // ping etcd ctx, cancel := context.WithTimeout(ctx, time.Second*10) @@ -81,65 +71,26 @@ func (s *disconnectState) ConnectCommand(ctx context.Context, cp *ConnectParams) fmt.Println("Using meta path:", fmt.Sprintf("%s/%s/", cp.RootPath, metaPath)) // use rootPath as instanceName - s.SetNext(getInstanceState(etcdCli, cp.RootPath, cp.MetaPath, etcdState, s.config)) + app.SetTagNext(etcdTag, getInstanceState(app.core, etcdCli, cp.RootPath, cp.MetaPath, etcdState, app.config)) } else { fmt.Println("using dry mode, ignore rootPath and metaPath") // rootPath empty fall back to etcd connected state - s.SetNext(etcdState) + app.SetTagNext(etcdTag, etcdState) } return nil } -/* -func getUseCmd(cli clientv3.KV, state State, config *configs.Config) *cobra.Command { - cmd := &cobra.Command{ - Use: "use [instance name]", - Short: "use specified milvus instance", - Run: func(cmd *cobra.Command, args []string) { - if len(args) == 0 { - fmt.Println("instance name not provided") - cmd.Usage() - return - } - metaPath, err := cmd.Flags().GetString("metaPath") - if err != nil { - fmt.Println(err.Error()) - return - } - force, err := cmd.Flags().GetBool("force") - if err != nil { - fmt.Println(err.Error()) - return - } - rootPath := args[0] - - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - defer cancel() - err = pingEtcd(ctx, cli, rootPath, metaPath) - if err != nil { - if errors.Is(err, ErrNotMilvsuRootPath) { - if !force { - fmt.Printf("Connection established, but %s, please check your config or use Dry mode\n", err.Error()) - return - } - } else { - fmt.Println("failed to ping etcd", err.Error()) - return - } - } - - fmt.Printf("Using meta path: %s/%s/\n", rootPath, metaPath) - - state.SetNext(getInstanceState(cli, rootPath, state, config)) - }, - } - cmd.Flags().Bool("force", false, "force connect ignoring ping Etcd rootPath check") - cmd.Flags().String("metaPath", metaPath, "meta path prefix") - return cmd -}*/ +type ConnectParams struct { + framework.ParamBase `use:"connect" desc:"Connect to etcd"` + EtcdAddr string `name:"etcd" default:"127.0.0.1:2379" desc:"the etcd endpoint to connect"` + RootPath string `name:"rootPath" default:"by-dev" desc:"meta root paht milvus is using"` + MetaPath string `name:"metaPath" default:"meta" desc:"meta path prefix"` + Force bool `name:"force" default:"false" desc:"force connect ignoring ping Etcd & rootPath check"` + Dry bool `name:"dry" default:"false" desc:"dry connect without specifying milvus instance"` +} type etcdConnectedState struct { - cmdState + *framework.CmdState client *clientv3.Client addr string candidates []string @@ -149,37 +100,24 @@ type etcdConnectedState struct { // SetupCommands setups the command. // also called after each command run to reset flag values. func (s *etcdConnectedState) SetupCommands() { - cmd := &cobra.Command{} + cmd := s.GetCmd() - s.mergeFunctionCommands(cmd, s) - - s.cmdState.rootCmd = cmd - s.setupFn = s.SetupCommands + s.UpdateState(cmd, s, s.SetupCommands) } // getEtcdConnectedState returns etcdConnectedState for unknown instance -func getEtcdConnectedState(cli *clientv3.Client, addr string, config *configs.Config) State { +func getEtcdConnectedState(parent *framework.CmdState, cli *clientv3.Client, addr string, config *configs.Config) framework.State { state := &etcdConnectedState{ - cmdState: cmdState{ - label: fmt.Sprintf("Etcd(%s)", addr), - }, - client: cli, - addr: addr, - config: config, + CmdState: parent.Spawn(fmt.Sprintf("Etcd(%s)", addr)), + client: cli, + addr: addr, + config: config, } - state.SetupCommands() - return state } -func (s *etcdConnectedState) DisconnectCommand(ctx context.Context, p *DisconnectParam) error { - s.SetNext(Start(s.config)) - s.Close() - return nil -} - type FindMilvusParam struct { framework.ParamBase `use:"find-milvus" desc:"search etcd kvs to find milvus instance"` } @@ -228,7 +166,7 @@ func (s *etcdConnectedState) UseCommand(ctx context.Context, p *UseParam) error fmt.Printf("Using meta path: %s/%s/\n", p.instanceName, p.MetaPath) - s.SetNext(getInstanceState(s.client, p.instanceName, p.MetaPath, s, s.config)) + s.SetNext(getInstanceState(s.CmdState, s.client, p.instanceName, p.MetaPath, s, s.config)) return nil } diff --git a/states/exit.go b/states/exit.go deleted file mode 100644 index 106d182..0000000 --- a/states/exit.go +++ /dev/null @@ -1,55 +0,0 @@ -package states - -import ( - "context" - - "github.com/milvus-io/birdwatcher/framework" - "github.com/spf13/cobra" -) - -// ExitErr is the error indicates user needs to exit application. -var ExitErr = exitErr{} - -// exitErr internal err type for comparing. -type exitErr struct{} - -// Error implements error. -func (e exitErr) Error() string { - return "exited" -} - -// getExitCmd returns exit command for input state. -func getExitCmd(state State) *cobra.Command { - cmd := &cobra.Command{ - Use: "exit", - Short: "Closes the cli", - Aliases: []string{"quit"}, - RunE: func(*cobra.Command, []string) error { - state.SetNext(&exitState{}) - // cannot return ExitErr here to avoid print help message - return nil - }, - } - return cmd -} - -// exitState simple exit state. -type exitState struct { - cmdState -} - -// SetupCommands setups the command. -// also called after each command run to reset flag values. -func (s *exitState) SetupCommands() {} - -// IsEnding returns true for exit State -func (s *exitState) IsEnding() bool { return true } - -type DisconnectParam struct { - framework.ParamBase `use:"disconnect" desc:"disconnect from current etcd instance"` -} - -func (s *InstanceState) DisconnectCommand(ctx context.Context, _ *DisconnectParam) { - s.SetNext(Start(s.config)) - s.Close() -} diff --git a/states/instance.go b/states/instance.go index 68cd4dc..55be278 100644 --- a/states/instance.go +++ b/states/instance.go @@ -6,6 +6,7 @@ import ( "path" "github.com/milvus-io/birdwatcher/configs" + "github.com/milvus-io/birdwatcher/framework" "github.com/milvus-io/birdwatcher/states/etcd" "github.com/milvus-io/birdwatcher/states/etcd/audit" "github.com/milvus-io/birdwatcher/states/etcd/remove" @@ -16,14 +17,14 @@ import ( // InstanceState provides command for single milvus instance. type InstanceState struct { - cmdState + *framework.CmdState *show.ComponentShow *remove.ComponentRemove instanceName string client clientv3.KV auditFile *os.File - etcdState State + etcdState framework.State config *configs.Config basePath string } @@ -37,7 +38,7 @@ func (s *InstanceState) Close() { // SetupCommands setups the command. // also called after each command run to reset flag values. func (s *InstanceState) SetupCommands() { - cmd := &cobra.Command{} + cmd := s.GetCmd() cli := s.client instanceName := s.instanceName @@ -112,13 +113,12 @@ func (s *InstanceState) SetupCommands() { ) //cmd.AddCommand(etcd.RawCommands(cli)...) - s.mergeFunctionCommands(cmd, s) - s.cmdState.rootCmd = cmd - s.setupFn = s.SetupCommands + + s.UpdateState(cmd, s, s.SetupCommands) } // getDryModeCmd enter dry-mode -func getDryModeCmd(cli clientv3.KV, state *InstanceState, etcdState State) *cobra.Command { +func getDryModeCmd(cli clientv3.KV, state *InstanceState, etcdState framework.State) *cobra.Command { cmd := &cobra.Command{ Use: "dry-mode", Short: "enter dry mode to select instance", @@ -129,7 +129,7 @@ func getDryModeCmd(cli clientv3.KV, state *InstanceState, etcdState State) *cobr return cmd } -func getInstanceState(cli clientv3.KV, instanceName, metaPath string, etcdState State, config *configs.Config) State { +func getInstanceState(parent *framework.CmdState, cli clientv3.KV, instanceName, metaPath string, etcdState framework.State, config *configs.Config) framework.State { var kv clientv3.KV file, err := os.OpenFile("audit.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, os.ModeAppend) if err != nil { @@ -143,9 +143,7 @@ func getInstanceState(cli clientv3.KV, instanceName, metaPath string, etcdState // use audit kv state := &InstanceState{ - cmdState: cmdState{ - label: fmt.Sprintf("Milvus(%s)", instanceName), - }, + CmdState: parent.Spawn(fmt.Sprintf("Milvus(%s)", instanceName)), ComponentShow: show.NewComponent(cli, config, basePath), ComponentRemove: remove.NewComponent(cli, config, basePath), instanceName: instanceName, @@ -157,7 +155,5 @@ func getInstanceState(cli clientv3.KV, instanceName, metaPath string, etcdState basePath: basePath, } - state.SetupCommands() - return state } diff --git a/states/load_backup.go b/states/load_backup.go index 860adc2..4b60065 100644 --- a/states/load_backup.go +++ b/states/load_backup.go @@ -35,7 +35,7 @@ func (p *LoadBackupParam) ParseArgs(args []string) error { return nil } -func (s *disconnectState) LoadBackupCommand(ctx context.Context, p *LoadBackupParam) error { +func (app *ApplicationState) LoadBackupCommand(ctx context.Context, p *LoadBackupParam) error { f, err := openBackupFile(p.backupFile) if err != nil { return err @@ -62,7 +62,7 @@ func (s *disconnectState) LoadBackupCommand(ctx context.Context, p *LoadBackupPa fileName := path.Base(p.backupFile) p.WorkspaceName = fileName } - p.WorkspaceName = createWorkspaceFolder(s.config, p.WorkspaceName) + p.WorkspaceName = createWorkspaceFolder(app.config, p.WorkspaceName) } server, err := startEmbedEtcdServer(p.WorkspaceName, p.UseWorkspace) @@ -71,8 +71,8 @@ func (s *disconnectState) LoadBackupCommand(ctx context.Context, p *LoadBackupPa return err } fmt.Println("using data dir:", server.Config().Dir) - // TODO - nextState := getEmbedEtcdInstanceV2(server, s.config) + + nextState := getEmbedEtcdInstanceV2(app.core, server, app.config) switch header.Version { case 1: fmt.Printf("Found backup version: %d, instance name :%s\n", header.Version, header.Instance) @@ -101,7 +101,7 @@ func (s *disconnectState) LoadBackupCommand(ctx context.Context, p *LoadBackupPa return err } - s.SetNext(nextState) + app.SetTagNext(etcdTag, nextState) return nil } diff --git a/states/open.go b/states/open.go index 534cbb6..357b399 100644 --- a/states/open.go +++ b/states/open.go @@ -28,9 +28,9 @@ func (p *OpenParam) ParseArgs(args []string) error { } // OpenCommand implements open workspace command -func (s *disconnectState) OpenCommand(ctx context.Context, p *OpenParam) error { +func (app *ApplicationState) OpenCommand(ctx context.Context, p *OpenParam) error { workspaceName := p.workspaceName - workPath := path.Join(s.config.WorkspacePath, workspaceName) + workPath := path.Join(app.config.WorkspacePath, workspaceName) info, err := os.Stat(workPath) if os.IsNotExist(err) { fmt.Printf("workspace %s not exist\n", workspaceName) @@ -45,12 +45,12 @@ func (s *disconnectState) OpenCommand(ctx context.Context, p *OpenParam) error { return fmt.Errorf("failed to start embed etcd server in workspace %s, err: %s", workspaceName, err.Error()) } - nextState := getEmbedEtcdInstanceV2(server, s.config) + nextState := getEmbedEtcdInstanceV2(app.core, server, app.config) err = nextState.setupWorkDir(workPath) if err != nil { return fmt.Errorf("failed to setup workspace for %s, err: %s", workspaceName, err.Error()) } - s.SetNext(nextState) + app.SetTagNext(etcdTag, nextState) return nil } diff --git a/states/parse_file.go b/states/parse_file.go index 038e01f..61d50b3 100644 --- a/states/parse_file.go +++ b/states/parse_file.go @@ -33,7 +33,7 @@ func (p *ParseIndexParam) ParseArgs(args []string) error { } // ParseIndexParamCommand parses index params from file. -func (s *disconnectState) ParseIndexParamCommand(ctx context.Context, p *ParseIndexParam) error { +func (app *ApplicationState) ParseIndexParamCommand(ctx context.Context, p *ParseIndexParam) error { f, err := openBackupFile(p.filePath) if err != nil { return err @@ -85,7 +85,7 @@ func (p *ValidateIndexParam) ParseArgs(args []string) error { return nil } -func (s *disconnectState) ValidateIndexFilesCommand(ctx context.Context, p *ValidateIndexParam) error { +func (app *ApplicationState) ValidateIndexFilesCommand(ctx context.Context, p *ValidateIndexParam) error { folder := p.directory if err := testFolder(folder); err != nil { return err @@ -188,7 +188,7 @@ func (p *AssembleIndexFilesParam) ParseArgs(args []string) error { return nil } -func (s *disconnectState) AssembleIndexFilesCommand(ctx context.Context, p *AssembleIndexFilesParam) error { +func (app *ApplicationState) AssembleIndexFilesCommand(ctx context.Context, p *AssembleIndexFilesParam) error { folder := p.directory if err := testFolder(folder); err != nil { return err diff --git a/states/pulsarctl_connect.go b/states/pulsarctl_connect.go index a74c1fb..39abb0e 100644 --- a/states/pulsarctl_connect.go +++ b/states/pulsarctl_connect.go @@ -4,8 +4,8 @@ import ( "context" "fmt" + "github.com/cockroachdb/errors" "github.com/milvus-io/birdwatcher/framework" - "github.com/spf13/cobra" pulsarctl "github.com/streamnative/pulsarctl/pkg/pulsar" "github.com/streamnative/pulsarctl/pkg/pulsar/common" "github.com/streamnative/pulsarctl/pkg/pulsar/utils" @@ -18,7 +18,7 @@ type PulsarctlParam struct { AuthParam string `name:"authParam" default:"" desc:"pulsar admin auth parameters"` } -func (s *disconnectState) PulsarctlCommand(ctx context.Context, p *PulsarctlParam) error { +func (app *ApplicationState) PulsarctlCommand(ctx context.Context, p *PulsarctlParam) error { config := common.Config{ WebServiceURL: p.Address, @@ -32,66 +32,22 @@ func (s *disconnectState) PulsarctlCommand(ctx context.Context, p *PulsarctlPara return err } - adminState := getPulsarAdminState(admin, p.Address) - s.SetNext(adminState) + adminState := getPulsarAdminState(app.core, admin, p.Address) + app.SetTagNext(pulsarTag, adminState) return nil } -func getPulsarctlCmd(state State) *cobra.Command { - cmd := &cobra.Command{ - Use: "pulsarctl", - Short: "connect to pulsar admin with pulsarctl", - Run: func(cmd *cobra.Command, args []string) { - address, err := cmd.Flags().GetString("addr") - if err != nil { - fmt.Println(err.Error()) - } - authPlugin, err := cmd.Flags().GetString("authPlugin") - if err != nil { - fmt.Println(err.Error()) - } - authParams, err := cmd.Flags().GetString("authParams") - if err != nil { - fmt.Println(err.Error()) - } - - config := common.Config{ - WebServiceURL: address, - AuthPlugin: authPlugin, - AuthParams: authParams, - PulsarAPIVersion: common.V2, - } - admin, err := pulsarctl.New(&config) - if err != nil { - fmt.Println("failed to build pulsar admin client, error:", err.Error()) - } - - adminState := getPulsarAdminState(admin, address) - state.SetNext(adminState) - }, - } - - cmd.Flags().String("addr", "http://localhost:18080", "pulsar admin address") - cmd.Flags().String("authPlugin", "", "pulsar admin auth plugin") - cmd.Flags().String("authParams", "", "pulsar admin auth parameters") - - return cmd -} - -func getPulsarAdminState(admin pulsarctl.Client, addr string) State { +func getPulsarAdminState(parent *framework.CmdState, admin pulsarctl.Client, addr string) framework.State { state := &pulsarAdminState{ - cmdState: cmdState{ - label: fmt.Sprintf("PulsarAdmin(%s)", addr), - }, - admin: admin, - addr: addr, + CmdState: parent.Spawn(fmt.Sprintf("PulsarAdmin(%s)", addr)), + admin: admin, + addr: addr, } - state.SetupCommands() return state } type pulsarAdminState struct { - cmdState + *framework.CmdState admin pulsarctl.Client addr string @@ -101,78 +57,57 @@ type pulsarAdminState struct { // SetupCommands setups the command. // also called after each command run to reset flag values. func (s *pulsarAdminState) SetupCommands() { - cmd := &cobra.Command{} - - cmd.AddCommand( + cmd := s.GetCmd() - getListTopicCmd(s.admin), - - getListSubscriptionCmd(s.admin), - - getExitCmd(s), - ) + s.UpdateState(cmd, s, s.SetupCommands) +} - s.cmdState.rootCmd = cmd - s.setupFn = s.SetupCommands +type ListTopicParam struct { + framework.ParamBase `use:"list topic" desc:"list topics in instance"` + Tenant string `name:"tenant" default:"public" desc:"tenant name to list"` + Namespace string `name:"namespace" default:"default" desc:"namespace name to list"` } -func getListTopicCmd(admin pulsarctl.Client) *cobra.Command { - cmd := &cobra.Command{ - Use: "list-topic", - Short: "list topics in instance", - Run: func(cmd *cobra.Command, args []string) { - nsn, err := utils.GetNameSpaceName("public", "default") - if err != nil { - fmt.Println(err.Error()) - return - } - np, p, err := admin.Topics().List(*nsn) - if err != nil { - fmt.Println("failed to list topics", err.Error()) - return - } - fmt.Println("Non-persist topics:") - for _, topic := range np { - fmt.Println(topic) - } - fmt.Println("Persist topics:") - for _, topic := range p { - fmt.Println(topic) - } - }, +func (s *pulsarAdminState) ListTopicCommand(ctx context.Context, p *ListTopicParam) error { + nsn, err := utils.GetNameSpaceName(p.Tenant, p.Namespace) + if err != nil { + return err } + npt, pt, err := s.admin.Topics().List(*nsn) + if err != nil { + return errors.Wrap(err, "failed to list topics") + } + fmt.Println("Non-persist topics:") + for _, topic := range npt { + fmt.Println(topic) + } + fmt.Println("Persist topics:") + for _, topic := range pt { + fmt.Println(topic) + } + return nil +} - return cmd +type ListSubscriptionParam struct { + framework.ParamBase `use:"list subscription" desc:"list subscriptions for provided topic"` + Tenant string `name:"tenant" default:"public" desc:"tenant name to list"` + Namespace string `name:"namespace" default:"default" desc:"namespace name to list"` + Topic string `name:"topic" default:"" desc:"topic to check subscription"` } -func getListSubscriptionCmd(admin pulsarctl.Client) *cobra.Command { - cmd := &cobra.Command{ - Use: "list-subscription", - Short: "list topic subscriptions", - Run: func(cmd *cobra.Command, args []string) { - topic, err := cmd.Flags().GetString("topic") - if err != nil { - fmt.Println(err.Error()) - return - } - - topicName, err := utils.GetTopicName(topic) - if err != nil { - fmt.Println("failed to parse topic name", err.Error()) - return - } - - subscriptions, err := admin.Subscriptions().List(*topicName) - if err != nil { - fmt.Println("failed to list subscriptions", err.Error()) - return - } - for _, subName := range subscriptions { - fmt.Println(subName) - } - }, +func (s *pulsarAdminState) ListSubscriptionCommand(ctx context.Context, p *ListSubscriptionParam) error { + + topicName, err := utils.GetTopicName(fmt.Sprintf("%s/%s/%s", p.Tenant, p.Namespace, p.Topic)) + if err != nil { + return errors.Wrap(err, "failed to parse topic name") } - cmd.Flags().String("topic", "", "target topic to list") - return cmd + subscriptions, err := s.admin.Subscriptions().List(*topicName) + if err != nil { + return errors.Wrap(err, "failed to list subscriptions") + } + for _, subName := range subscriptions { + fmt.Println(subName) + } + return nil } diff --git a/states/start.go b/states/start.go index bf9468d..4f35c37 100644 --- a/states/start.go +++ b/states/start.go @@ -2,28 +2,28 @@ package states import ( "github.com/milvus-io/birdwatcher/configs" + "github.com/milvus-io/birdwatcher/framework" "github.com/milvus-io/birdwatcher/models" etcdversion "github.com/milvus-io/birdwatcher/states/etcd/version" ) +const ( + etcdTag = "etcd" + pulsarTag = "pulsar" + minioTag = "minio" +) + // Start returns the first state - offline. -func Start(config *configs.Config) State { +func Start(config *configs.Config) framework.State { app := &ApplicationState{ - State: getDisconnectedState(config), + states: map[string]framework.State{}, config: config, } + app.core = framework.NewCmdState("Offline") + app.SetupCommands() + etcdversion.SetVersion(models.GTEVersion2_2) return app } - -// ApplicationState application background state. -// used for state switch/merging. -type ApplicationState struct { - // current state - State - - // config stores configuration items - config *configs.Config -} diff --git a/states/storage/fs.go b/states/storage/fs.go new file mode 100644 index 0000000..76bde6c --- /dev/null +++ b/states/storage/fs.go @@ -0,0 +1,141 @@ +package storage + +import ( + "context" + "errors" + "fmt" + "path" + "strings" + + "github.com/fatih/color" + "github.com/milvus-io/birdwatcher/framework" + "github.com/minio/minio-go/v7" +) + +func (s *MinioState) getBase() string { + base := s.prefix + if !strings.HasSuffix(base, "/") { + base += "/" + } + + return strings.TrimPrefix(base, "/") +} + +type LsParam struct { + framework.ParamBase `use:"ls" desc:"ls file/folder"` + + prefix string +} + +func (p *LsParam) ParseArgs(args []string) error { + if len(args) == 0 { + return nil + } + + if len(args) > 1 { + return errors.New("too many parameters") + } + + p.prefix = args[0] + + return nil +} + +func (s *MinioState) LsCommand(ctx context.Context, p *LsParam) error { + base := s.getBase() + ch := s.client.ListObjects(ctx, s.bucket, minio.ListObjectsOptions{ + Prefix: base, + Recursive: false, + }) + + dc := color.New(color.FgCyan) + fc := color.New(color.FgGreen) + for info := range ch { + name := strings.TrimPrefix(info.Key, base) + c := fc + isDirectory := false + if strings.HasSuffix(name, "/") { + name = strings.TrimSuffix(name, "/") + c = dc + isDirectory = true + } + fmt.Printf("%s", c.Sprint(name)) + if !isDirectory { + fmt.Printf("\t Size: %d", info.Size) + } + fmt.Println() + } + return nil +} + +type CdParam struct { + framework.ParamBase `use:"cd" desc:"ls file/folder"` + + prefix string +} + +func (p *CdParam) ParseArgs(args []string) error { + if len(args) == 0 { + return nil + } + + if len(args) > 1 { + return errors.New("too many parameters") + } + + p.prefix = args[0] + + return nil +} + +func (s *MinioState) CdCommand(ctx context.Context, p *CdParam) error { + + base := s.getBase() + + // use absolute path + if strings.HasPrefix(p.prefix, "/") { + base = path.Dir(strings.TrimPrefix(p.prefix, "/")) + } else { + base = path.Join(base, path.Dir(p.prefix)) + } + if base == "." { + base = "" + } + p.prefix = strings.TrimSuffix(path.Base(p.prefix), "/") + if !strings.HasSuffix(base, "/") { + base += "/" + } + + ch := s.client.ListObjects(ctx, s.bucket, minio.ListObjectsOptions{ + Prefix: base, + Recursive: false, + }) + + folders := make(map[string]struct{}) + + for info := range ch { + name := strings.TrimPrefix(info.Key, base) + if strings.HasSuffix(name, "/") { + name = strings.TrimSuffix(name, "/") + folders[name] = struct{}{} + } + } + + fmt.Println(base, p.prefix, folders) + if _, ok := folders[p.prefix]; !ok { + return fmt.Errorf("folder %s not exists", p.prefix) + } + + s.prefix = path.Join(base, p.prefix) + + return nil +} + +type PwdParam struct { + framework.ParamBase `use:"pwd" desc:"print current working directory path for minio"` +} + +func (s *MinioState) PwdCommand(ctx context.Context, p *PwdParam) error { + fmt.Println(s.getBase()) + return nil +} diff --git a/states/storage/minio.go b/states/storage/minio.go new file mode 100644 index 0000000..3c735b7 --- /dev/null +++ b/states/storage/minio.go @@ -0,0 +1,71 @@ +package storage + +import ( + "context" + "fmt" + + "github.com/milvus-io/birdwatcher/framework" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + "github.com/spf13/cobra" +) + +type MinioState struct { + *framework.CmdState + + client *minio.Client + bucket string + prefix string +} + +func (s *MinioState) SetupCommands() { + cmd := &cobra.Command{} + + s.MergeFunctionCommands(cmd, s) + s.CmdState.RootCmd = cmd + s.SetupFn = s.SetupCommands +} + +type ConnectMinioParam struct { + framework.ParamBase `use:"connect-minio" desc:"connect to minio instance"` + Bucket string `name:"bucket" default:"" desc:"bucket name"` + Address string `name:"address" default:"127.0.0.1:9000" desc:"minio address to connect"` + UseIAM bool `name:"iam" default:"false" desc:"use IAM mode"` + IAMEndpoint string `name:"iamEndpoint" default:"" desc:"IAM endpoint address"` + AK string `name:"ak" default:"" desc:"access key/username"` + SK string `name:"sk" default:"" desc:"secret key/password"` + UseSSL bool `name:"ssl" default:"" desc:"use SSL"` +} + +func ConnectMinio(ctx context.Context, p *ConnectMinioParam) (*MinioState, error) { + var cred *credentials.Credentials + if p.UseIAM { + cred = credentials.NewIAM(p.IAMEndpoint) + } else { + cred = credentials.NewStaticV4(p.AK, p.SK, "") + } + + minioClient, err := minio.New(p.Address, &minio.Options{ + Creds: cred, + Secure: p.UseSSL, + }) + + if err != nil { + return nil, err + } + + exists, err := minioClient.BucketExists(ctx, p.Bucket) + if err != nil { + return nil, err + } + + if !exists { + return nil, fmt.Errorf("bucket %s not exists", p.Bucket) + } + + return &MinioState{ + client: minioClient, + bucket: p.Bucket, + CmdState: framework.NewCmdState("Minio(Addr)"), + }, nil +} diff --git a/states/util.go b/states/util.go index 263b719..aedff19 100644 --- a/states/util.go +++ b/states/util.go @@ -50,7 +50,7 @@ func (p *ParseTSParam) ParseArgs(args []string) error { return nil } -func (s *cmdState) ParseTSCommand(ctx context.Context, p *ParseTSParam) { +func (app *ApplicationState) ParseTSCommand(ctx context.Context, p *ParseTSParam) { if len(p.args) == 0 { fmt.Println("no ts provided") } @@ -71,7 +71,7 @@ type PrintVerParam struct { framework.ParamBase `use:"version" desc:"print version"` } -func (s *cmdState) PrintVersionCommand(ctx context.Context, _ *PrintVerParam) { +func (app *ApplicationState) PrintVersionCommand(ctx context.Context, _ *PrintVerParam) { fmt.Println("Birdwatcher Version", common.Version) } diff --git a/states/visit.go b/states/visit.go index 849b4a4..b761acb 100644 --- a/states/visit.go +++ b/states/visit.go @@ -6,6 +6,7 @@ import ( "fmt" "strconv" + "github.com/milvus-io/birdwatcher/framework" "github.com/milvus-io/birdwatcher/models" "github.com/milvus-io/birdwatcher/proto/v2.0/commonpb" "github.com/milvus-io/birdwatcher/proto/v2.0/datapb" @@ -34,7 +35,7 @@ func getSessionTypes() []string { } } -func getVisitCmd(state State, cli clientv3.KV, basePath string) *cobra.Command { +func getVisitCmd(state framework.State, cli clientv3.KV, basePath string) *cobra.Command { callCmd := &cobra.Command{ Use: "visit", Short: "enter state that could visit some service of component", @@ -47,8 +48,7 @@ func getVisitCmd(state State, cli clientv3.KV, basePath string) *cobra.Command { return callCmd } -func setNextState(sessionType string, conn *grpc.ClientConn, statePtr *State, session *models.Session) { - state := *statePtr +func setNextState(sessionType string, conn *grpc.ClientConn, state framework.State, session *models.Session) { switch sessionType { case "datacoord": client := datapb.NewDataCoordClient(conn) @@ -100,7 +100,7 @@ func getSessionConnect(cli clientv3.KV, basePath string, id int64, sessionType s return nil, nil, errors.New("invalid id") } -func getVisitSessionCmds(state State, cli clientv3.KV, basePath string) []*cobra.Command { +func getVisitSessionCmds(state framework.State, cli clientv3.KV, basePath string) []*cobra.Command { sessionCmds := make([]*cobra.Command, 0, len(getSessionTypes())) sessionTypes := getSessionTypes() @@ -136,7 +136,7 @@ func getVisitSessionCmds(state State, cli clientv3.KV, basePath string) []*cobra fmt.Println(err.Error()) return } - setNextState(sessionType, conn, &state, &models.Session{ + setNextState(sessionType, conn, state, &models.Session{ Address: addr, }) return @@ -145,7 +145,7 @@ func getVisitSessionCmds(state State, cli clientv3.KV, basePath string) []*cobra if err != nil { return } - setNextState(sessionType, conn, &state, session) + setNextState(sessionType, conn, state, session) } } diff --git a/states/visit_datacoord.go b/states/visit_datacoord.go index 758c8a4..20e63f5 100644 --- a/states/visit_datacoord.go +++ b/states/visit_datacoord.go @@ -3,6 +3,7 @@ package states import ( "fmt" + "github.com/milvus-io/birdwatcher/framework" "github.com/milvus-io/birdwatcher/models" "github.com/milvus-io/birdwatcher/proto/v2.0/datapb" datapbv2 "github.com/milvus-io/birdwatcher/proto/v2.2/datapb" @@ -11,12 +12,12 @@ import ( ) type dataCoordState struct { - cmdState + *framework.CmdState session *models.Session client datapb.DataCoordClient clientv2 datapbv2.DataCoordClient conn *grpc.ClientConn - prevState State + prevState framework.State } // SetupCommands setups the command. @@ -35,18 +36,16 @@ func (s *dataCoordState) SetupCommands() { getExitCmd(s), ) - s.mergeFunctionCommands(cmd, s) + s.MergeFunctionCommands(cmd, s) - s.cmdState.rootCmd = cmd - s.setupFn = s.SetupCommands + s.CmdState.RootCmd = cmd + s.SetupFn = s.SetupCommands } -func getDataCoordState(client datapb.DataCoordClient, conn *grpc.ClientConn, prev State, session *models.Session) State { +func getDataCoordState(client datapb.DataCoordClient, conn *grpc.ClientConn, prev framework.State, session *models.Session) framework.State { state := &dataCoordState{ - cmdState: cmdState{ - label: fmt.Sprintf("DataCoord-%d(%s)", session.ServerID, session.Address), - }, + CmdState: framework.NewCmdState(fmt.Sprintf("DataCoord-%d(%s)", session.ServerID, session.Address)), session: session, client: client, clientv2: datapbv2.NewDataCoordClient(conn), diff --git a/states/visit_datanode.go b/states/visit_datanode.go index 9e52f99..f466463 100644 --- a/states/visit_datanode.go +++ b/states/visit_datanode.go @@ -3,6 +3,7 @@ package states import ( "fmt" + "github.com/milvus-io/birdwatcher/framework" "github.com/milvus-io/birdwatcher/models" "github.com/milvus-io/birdwatcher/proto/v2.0/datapb" datapbv2 "github.com/milvus-io/birdwatcher/proto/v2.2/datapb" @@ -11,12 +12,12 @@ import ( ) type dataNodeState struct { - cmdState + *framework.CmdState session *models.Session client datapb.DataNodeClient clientv2 datapbv2.DataNodeClient conn *grpc.ClientConn - prevState State + prevState framework.State } // SetupCommands setups the command. @@ -34,18 +35,16 @@ func (s *dataNodeState) SetupCommands() { getExitCmd(s), ) - s.mergeFunctionCommands(cmd, s) + s.MergeFunctionCommands(cmd, s) - s.cmdState.rootCmd = cmd - s.setupFn = s.SetupCommands + s.CmdState.RootCmd = cmd + s.SetupFn = s.SetupCommands } -func getDataNodeState(client datapb.DataNodeClient, conn *grpc.ClientConn, prev State, session *models.Session) State { +func getDataNodeState(client datapb.DataNodeClient, conn *grpc.ClientConn, prev framework.State, session *models.Session) framework.State { state := &dataNodeState{ - cmdState: cmdState{ - label: fmt.Sprintf("DataNode-%d(%s)", session.ServerID, session.Address), - }, + CmdState: framework.NewCmdState(fmt.Sprintf("DataNode-%d(%s)", session.ServerID, session.Address)), session: session, client: client, clientv2: datapbv2.NewDataNodeClient(conn), diff --git a/states/visit_indexcoord.go b/states/visit_indexcoord.go index 0497a56..4b99f95 100644 --- a/states/visit_indexcoord.go +++ b/states/visit_indexcoord.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/milvus-io/birdwatcher/framework" "github.com/milvus-io/birdwatcher/models" "github.com/milvus-io/birdwatcher/proto/v2.0/indexpb" indexpbv2 "github.com/milvus-io/birdwatcher/proto/v2.2/indexpb" @@ -13,12 +14,12 @@ import ( ) type indexCoordState struct { - cmdState + *framework.CmdState session *models.Session client indexpb.IndexCoordClient clientv2 indexpbv2.IndexCoordClient conn *grpc.ClientConn - prevState State + prevState framework.State } // SetupCommands setups the command. @@ -38,18 +39,16 @@ func (s *indexCoordState) SetupCommands() { getExitCmd(s), ) - s.mergeFunctionCommands(cmd, s) + s.MergeFunctionCommands(cmd, s) - s.cmdState.rootCmd = cmd - s.setupFn = s.SetupCommands + s.CmdState.RootCmd = cmd + s.SetupFn = s.SetupCommands } -func getIndexCoordState(client indexpb.IndexCoordClient, conn *grpc.ClientConn, prev State, session *models.Session) State { +func getIndexCoordState(client indexpb.IndexCoordClient, conn *grpc.ClientConn, prev framework.State, session *models.Session) framework.State { state := &indexCoordState{ - cmdState: cmdState{ - label: fmt.Sprintf("IndexCoord-%d(%s)", session.ServerID, session.Address), - }, + CmdState: framework.NewCmdState(fmt.Sprintf("IndexCoord-%d(%s)", session.ServerID, session.Address)), session: session, client: client, clientv2: indexpbv2.NewIndexCoordClient(conn), diff --git a/states/visit_indexnode.go b/states/visit_indexnode.go index 4b5b40a..6f4a27a 100644 --- a/states/visit_indexnode.go +++ b/states/visit_indexnode.go @@ -3,6 +3,7 @@ package states import ( "fmt" + "github.com/milvus-io/birdwatcher/framework" "github.com/milvus-io/birdwatcher/models" "github.com/milvus-io/birdwatcher/proto/v2.0/indexpb" indexpbv2 "github.com/milvus-io/birdwatcher/proto/v2.2/indexpb" @@ -11,12 +12,12 @@ import ( ) type indexNodeState struct { - cmdState + *framework.CmdState session *models.Session client indexpb.IndexNodeClient clientv2 indexpbv2.IndexNodeClient conn *grpc.ClientConn - prevState State + prevState framework.State } // SetupCommands setups the command. @@ -33,17 +34,15 @@ func (s *indexNodeState) SetupCommands() { // exit getExitCmd(s), ) - s.mergeFunctionCommands(cmd, s) + s.MergeFunctionCommands(cmd, s) - s.cmdState.rootCmd = cmd - s.setupFn = s.SetupCommands + s.CmdState.RootCmd = cmd + s.SetupFn = s.SetupCommands } -func getIndexNodeState(client indexpb.IndexNodeClient, conn *grpc.ClientConn, prev State, session *models.Session) State { +func getIndexNodeState(client indexpb.IndexNodeClient, conn *grpc.ClientConn, prev framework.State, session *models.Session) framework.State { state := &indexNodeState{ - cmdState: cmdState{ - label: fmt.Sprintf("IndexNode-%d(%s)", session.ServerID, session.Address), - }, + CmdState: framework.NewCmdState(fmt.Sprintf("IndexNode-%d(%s)", session.ServerID, session.Address)), session: session, client: client, clientv2: indexpbv2.NewIndexNodeClient(conn), diff --git a/states/visit_querycoord.go b/states/visit_querycoord.go index a4454c1..c5f9f44 100644 --- a/states/visit_querycoord.go +++ b/states/visit_querycoord.go @@ -3,6 +3,7 @@ package states import ( "fmt" + "github.com/milvus-io/birdwatcher/framework" "github.com/milvus-io/birdwatcher/models" "github.com/milvus-io/birdwatcher/proto/v2.0/querypb" querypbv2 "github.com/milvus-io/birdwatcher/proto/v2.2/querypb" @@ -11,12 +12,12 @@ import ( ) type queryCoordState struct { - cmdState + *framework.CmdState session *models.Session client querypb.QueryCoordClient clientv2 querypbv2.QueryCoordClient conn *grpc.ClientConn - prevState State + prevState framework.State } // SetupCommands setups the command. @@ -33,10 +34,10 @@ func (s *queryCoordState) SetupCommands() { // exit getExitCmd(s), ) - s.mergeFunctionCommands(cmd, s) + s.MergeFunctionCommands(cmd, s) - s.cmdState.rootCmd = cmd - s.setupFn = s.SetupCommands + s.CmdState.RootCmd = cmd + s.SetupFn = s.SetupCommands } /* @@ -71,12 +72,10 @@ func (s *queryCoordState) ShowCollectionCmd() *cobra.Command { return cmd }*/ -func getQueryCoordState(client querypb.QueryCoordClient, conn *grpc.ClientConn, prev State, session *models.Session) State { +func getQueryCoordState(client querypb.QueryCoordClient, conn *grpc.ClientConn, prev framework.State, session *models.Session) framework.State { state := &queryCoordState{ - cmdState: cmdState{ - label: fmt.Sprintf("QueryCoord-%d(%s)", session.ServerID, session.Address), - }, + CmdState: framework.NewCmdState(fmt.Sprintf("QueryCoord-%d(%s)", session.ServerID, session.Address)), session: session, client: client, clientv2: querypbv2.NewQueryCoordClient(conn), diff --git a/states/visit_querynode.go b/states/visit_querynode.go index 343b658..b2428f7 100644 --- a/states/visit_querynode.go +++ b/states/visit_querynode.go @@ -5,6 +5,7 @@ import ( "fmt" "strconv" + "github.com/milvus-io/birdwatcher/framework" "github.com/milvus-io/birdwatcher/models" "github.com/milvus-io/birdwatcher/proto/v2.0/commonpb" "github.com/milvus-io/birdwatcher/proto/v2.0/querypb" @@ -16,12 +17,12 @@ import ( ) type queryNodeState struct { - cmdState + *framework.CmdState session *models.Session client querypb.QueryNodeClient clientv2 querypbv2.QueryNodeClient conn *grpc.ClientConn - prevState State + prevState framework.State } // SetupCommands setups the command. @@ -45,18 +46,16 @@ func (s *queryNodeState) SetupCommands() { getExitCmd(s), ) - s.mergeFunctionCommands(cmd, s) + s.MergeFunctionCommands(cmd, s) - s.cmdState.rootCmd = cmd - s.setupFn = s.SetupCommands + s.CmdState.RootCmd = cmd + s.SetupFn = s.SetupCommands } -func getQueryNodeState(client querypb.QueryNodeClient, conn *grpc.ClientConn, prev State, session *models.Session) State { +func getQueryNodeState(client querypb.QueryNodeClient, conn *grpc.ClientConn, prev framework.State, session *models.Session) framework.State { state := &queryNodeState{ - cmdState: cmdState{ - label: fmt.Sprintf("QueryNode-%d(%s)", session.ServerID, session.Address), - }, + CmdState: framework.NewCmdState(fmt.Sprintf("QueryNode-%d(%s)", session.ServerID, session.Address)), session: session, client: client, clientv2: querypbv2.NewQueryNodeClient(conn), @@ -69,7 +68,7 @@ func getQueryNodeState(client querypb.QueryNodeClient, conn *grpc.ClientConn, pr return state } -func getBackCmd(state, prev State) *cobra.Command { +func getBackCmd(state, prev framework.State) *cobra.Command { return &cobra.Command{ Use: "back", Run: func(cmd *cobra.Command, args []string) { diff --git a/states/visit_rootcoord.go b/states/visit_rootcoord.go index 5f18217..3d81770 100644 --- a/states/visit_rootcoord.go +++ b/states/visit_rootcoord.go @@ -3,6 +3,7 @@ package states import ( "fmt" + "github.com/milvus-io/birdwatcher/framework" "github.com/milvus-io/birdwatcher/models" "github.com/milvus-io/birdwatcher/proto/v2.0/rootcoordpb" rootcoordpbv2 "github.com/milvus-io/birdwatcher/proto/v2.2/rootcoordpb" @@ -11,12 +12,12 @@ import ( ) type rootCoordState struct { - cmdState + *framework.CmdState session *models.Session client rootcoordpb.RootCoordClient clientv2 rootcoordpbv2.RootCoordClient conn *grpc.ClientConn - prevState State + prevState framework.State } // SetupCommands setups the command. @@ -33,19 +34,17 @@ func (s *rootCoordState) SetupCommands() { // exit getExitCmd(s), ) - s.mergeFunctionCommands(cmd, s) + s.MergeFunctionCommands(cmd, s) - s.cmdState.rootCmd = cmd - s.setupFn = s.SetupCommands + s.CmdState.RootCmd = cmd + s.SetupFn = s.SetupCommands } -func getRootCoordState(client rootcoordpb.RootCoordClient, conn *grpc.ClientConn, prev State, session *models.Session) State { +func getRootCoordState(client rootcoordpb.RootCoordClient, conn *grpc.ClientConn, prev framework.State, session *models.Session) framework.State { state := &rootCoordState{ - session: session, - cmdState: cmdState{ - label: fmt.Sprintf("RootCoord-%d(%s)", session.ServerID, session.Address), - }, + session: session, + CmdState: framework.NewCmdState(fmt.Sprintf("RootCoord-%d(%s)", session.ServerID, session.Address)), client: client, clientv2: rootcoordpbv2.NewRootCoordClient(conn), conn: conn, diff --git a/states/web.go b/states/web.go index e8b5ff5..b6f761c 100644 --- a/states/web.go +++ b/states/web.go @@ -6,6 +6,7 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/milvus-io/birdwatcher/framework" "github.com/milvus-io/birdwatcher/states/etcd/common" etcdversion "github.com/milvus-io/birdwatcher/states/etcd/version" "github.com/spf13/cobra" @@ -13,7 +14,7 @@ import ( ) // getCmdCmd returns exit command for input state. -func getWebCmd(state State, cli clientv3.KV, basePath string) *cobra.Command { +func getWebCmd(state framework.State, cli clientv3.KV, basePath string) *cobra.Command { cmd := &cobra.Command{ Use: "web", Short: "start a web server to see more details on browser",