github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/admin/client.go (about) 1 package admin 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "net/http" 10 "time" 11 12 "github.com/pyroscope-io/pyroscope/pkg/api" 13 "github.com/pyroscope-io/pyroscope/pkg/model/appmetadata" 14 15 "github.com/hashicorp/go-multierror" 16 ) 17 18 type Client struct { 19 httpClient *http.Client 20 } 21 22 // TODO since this is shared between client/server 23 // maybe we could share it? 24 const ( 25 appsEndpoint = "http://pyroscope/v1/apps" 26 usersEndpoint = "http://pyroscope/v1/users" 27 storageEndpoint = "http://pyroscope/v1/storage" 28 ) 29 30 var ( 31 ErrHTTPClientCreation = errors.New("failed to create http over uds client") 32 ErrMakingRequest = errors.New("failed to make a request") 33 ErrStatusCodeNotOK = errors.New("failed to get a response with a valid status code") 34 ErrDecodingResponse = errors.New("error while decoding a (assumed) json response") 35 ErrMarshalingPayload = errors.New("error while marshalling the payload") 36 ) 37 38 func NewClient(socketAddr string, timeout time.Duration) (*Client, error) { 39 httpClient, err := NewHTTPOverUDSClient(socketAddr, WithTimeout(timeout)) 40 41 if err != nil { 42 return nil, multierror.Append(ErrHTTPClientCreation, err) 43 } 44 45 return &Client{ 46 httpClient, 47 }, nil 48 } 49 50 type AppNames []string 51 52 func (c *Client) GetAppsNames() (names AppNames, err error) { 53 resp, err := c.httpClient.Get(appsEndpoint) 54 if err != nil { 55 return names, multierror.Append(ErrMakingRequest, err) 56 } 57 58 if err := checkStatusCodeOK(resp.StatusCode); err != nil { 59 return names, multierror.Append(ErrStatusCodeNotOK, err) 60 } 61 62 // Strip all data except fqname 63 var apps []appmetadata.ApplicationMetadata 64 err = json.NewDecoder(resp.Body).Decode(&apps) 65 if err != nil { 66 return names, multierror.Append(ErrDecodingResponse, err) 67 } 68 69 appNames := make([]string, len(apps)) 70 for i, appName := range apps { 71 appNames[i] = appName.FQName 72 } 73 74 return appNames, nil 75 } 76 77 func (c *Client) DeleteApp(name string) (err error) { 78 payload := api.DeleteAppInput{Name: name} 79 80 marshalledPayload, err := json.Marshal(payload) 81 if err != nil { 82 return multierror.Append(ErrMarshalingPayload, err) 83 } 84 85 req, err := http.NewRequest(http.MethodDelete, appsEndpoint, bytes.NewBuffer(marshalledPayload)) 86 if err != nil { 87 return fmt.Errorf("error creating request: %w", err) 88 } 89 90 resp, err := c.httpClient.Do(req) 91 if err != nil { 92 return multierror.Append(ErrMakingRequest, err) 93 } 94 95 err = checkStatusCodeOK(resp.StatusCode) 96 if err != nil { 97 return multierror.Append(ErrStatusCodeNotOK, err) 98 } 99 return nil 100 } 101 102 func (c *Client) ResetUserPassword(username, password string, enable bool) error { 103 reqBody := UpdateUserRequest{Password: &password} 104 if enable { 105 reqBody.SetIsDisabled(false) 106 } 107 108 var b bytes.Buffer 109 if err := json.NewEncoder(&b).Encode(reqBody); err != nil { 110 return multierror.Append(ErrMarshalingPayload, err) 111 } 112 113 req, err := http.NewRequest(http.MethodPatch, usersEndpoint+"/"+username, &b) 114 if err != nil { 115 return fmt.Errorf("error creating request: %w", err) 116 } 117 return c.do(req) 118 } 119 120 func (c *Client) CleanupStorage() error { 121 req, err := http.NewRequest(http.MethodPut, storageEndpoint+"/cleanup", nil) 122 if err != nil { 123 return fmt.Errorf("error creating request: %w", err) 124 } 125 return c.do(req) 126 } 127 128 func (c *Client) do(req *http.Request) error { 129 resp, err := c.httpClient.Do(req) 130 if err != nil { 131 return err 132 } 133 if resp.StatusCode >= 200 && resp.StatusCode < 300 { 134 return nil 135 } 136 if v, err := io.ReadAll(resp.Body); err == nil { 137 return fmt.Errorf(string(v)) 138 } 139 return fmt.Errorf("received non 2xx status code: %d", resp.StatusCode) 140 } 141 142 func checkStatusCodeOK(statusCode int) error { 143 statusOK := statusCode >= 200 && statusCode < 300 144 if !statusOK { 145 return fmt.Errorf("Received non 2xx status code: %d", statusCode) 146 } 147 return nil 148 }