github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/admin/cli.go (about) 1 package admin 2 3 import ( 4 "bufio" 5 "errors" 6 "fmt" 7 "os" 8 "strings" 9 "time" 10 ) 11 12 type CLIError struct{ err error } 13 14 func (e CLIError) Error() string { 15 if errors.Is(e.err, ErrMakingRequest) { 16 return fmt.Sprintf(`failed to contact the admin socket server. 17 this may happen if 18 a) pyroscope server is not running 19 b) the socket path is incorrect 20 c) admin features are not enabled, in that case check the server flags 21 22 %v`, e.err) 23 } 24 25 return fmt.Sprintf("%v", e.err) 26 } 27 28 type CLI struct { 29 client *Client 30 } 31 32 func NewCLI(socketPath string, timeout time.Duration) (*CLI, error) { 33 client, err := NewClient(socketPath, timeout) 34 if err != nil { 35 return nil, err 36 } 37 38 return &CLI{ 39 client, 40 }, nil 41 } 42 43 // GetAppsNames returns the list of all apps 44 func (c *CLI) GetAppsNames() error { 45 appNames, err := c.client.GetAppsNames() 46 if err != nil { 47 return CLIError{err} 48 } 49 50 for _, name := range appNames { 51 fmt.Println(name) 52 } 53 54 return nil 55 } 56 57 // DeleteApp deletes an app if a matching app exists 58 func (c *CLI) DeleteApp(appname string, skipVerification bool) error { 59 if !skipVerification { 60 // since this is a very destructive action 61 // we ask the user to type it out the app name as a form of validation 62 fmt.Println(fmt.Sprintf("Are you sure you want to delete the app '%s'? This action can not be reversed.", appname)) 63 fmt.Println("") 64 fmt.Println("Keep in mind the following:") 65 fmt.Println("a) This command may take a while.") 66 fmt.Println("b) While it's running, it may lock the database for writes.") 67 fmt.Println("c) If an agent is still running, the app will be recreated.") 68 fmt.Println("d) The API is idempotent, ie. if the app already does NOT exist, this command will run just fine.") 69 fmt.Println("") 70 fmt.Println(fmt.Sprintf("Type '%s' to confirm (without quotes).", appname)) 71 reader := bufio.NewReader(os.Stdin) 72 text, err := reader.ReadString('\n') 73 if err != nil { 74 return err 75 } 76 trimmed := strings.TrimRight(text, "\n") 77 if trimmed != appname { 78 return fmt.Errorf("The app typed does not match. Want '%s' but got '%s'", appname, trimmed) 79 } 80 } 81 82 // finally delete the app 83 err := c.client.DeleteApp(appname) 84 if err != nil { 85 return CLIError{err} 86 } 87 88 fmt.Println(fmt.Sprintf("Deleted app '%s'.", appname)) 89 return nil 90 } 91 92 func (c *CLI) CleanupStorage() error { 93 return c.client.CleanupStorage() 94 } 95 96 func (c *CLI) ResetUserPassword(username, password string, enable bool) error { 97 if username == "" || password == "" { 98 return fmt.Errorf("username and password are required") 99 } 100 return c.client.ResetUserPassword(username, password, enable) 101 } 102 103 // CompleteApp returns the list of apps 104 // it's meant for cobra's autocompletion 105 // TODO use the parameter for fuzzy search? 106 func (c *CLI) CompleteApp(_ string) (appNames []string, err error) { 107 appNames, err = c.client.GetAppsNames() 108 109 if err != nil { 110 return nil, err 111 } 112 113 return appNames, err 114 }