package garage import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" ) // AdminClient is a helper type for performing actions against the garage admin // interface. type AdminClient struct { c *http.Client addr string adminToken string } // NewAdminClient initializes and returns an AdminClient which will use the // given address and adminToken for all requests made. func NewAdminClient(addr, adminToken string) *AdminClient { return &AdminClient{ c: &http.Client{ Transport: http.DefaultTransport.(*http.Transport).Clone(), }, addr: addr, adminToken: adminToken, } } // Do performs an HTTP request with the given method (GET, POST) and path, and // using the json marshaling of the given body as the request body (unless body // is nil). It will JSON unmarshal the response into rcv, unless rcv is nil. func (c *AdminClient) Do( ctx context.Context, rcv interface{}, method, path string, body interface{}, ) error { var bodyR io.Reader if body != nil { bodyBuf := new(bytes.Buffer) bodyR = bodyBuf if err := json.NewEncoder(bodyBuf).Encode(body); err != nil { return fmt.Errorf("json marshaling body: %w", err) } } urlStr := fmt.Sprintf("http://%s%s", c.addr, path) req, err := http.NewRequestWithContext(ctx, method, urlStr, bodyR) if err != nil { return fmt.Errorf("initializing request: %w", err) } req.Header.Set("Authorization", "Bearer "+c.adminToken) res, err := c.c.Do(req) if err != nil { return fmt.Errorf("performing http request: %w", err) } defer res.Body.Close() if res.StatusCode != 200 { return fmt.Errorf("unexpected %s response returned", res.Status) } if rcv == nil { if _, err := io.Copy(io.Discard, res.Body); err != nil { return fmt.Errorf("discarding response body: %w", err) } return nil } if err := json.NewDecoder(res.Body).Decode(rcv); err != nil { return fmt.Errorf("decoding json response body: %w", err) } return nil }