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  }