orchard/pkg/client/client.go

144 lines
2.6 KiB
Go

package client
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
)
var (
ErrFailed = errors.New("API client failed")
)
type Client struct {
address string
tlsConfig *tls.Config
httpClient *http.Client
baseURL *url.URL
}
type Config struct {
Address string
TLSConfig *tls.Config
}
func New(opts ...Option) (*Client, error) {
client := &Client{}
// Apply options
for _, opt := range opts {
opt(client)
}
// Apply defaults
if client.address == "" {
client.address = "http://127.0.0.1:6120"
}
// Instantiate client
httpClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: client.tlsConfig,
},
}
url, err := url.Parse(client.address)
if err != nil {
return nil, err
}
return &Client{
httpClient: httpClient,
baseURL: url,
}, nil
}
func (client *Client) request(
ctx context.Context,
method string,
path string,
in interface{},
out interface{},
params map[string]string,
) error {
var body io.Reader
if in != nil {
jsonBytes, err := json.Marshal(in)
if err != nil {
return fmt.Errorf("%w to marshal request body: %v", ErrFailed, err)
}
body = bytes.NewBuffer(jsonBytes)
}
endpointURL, err := url.Parse("v1/" + path)
if err != nil {
return fmt.Errorf("%w to parse API endpoint path: %v", ErrFailed, err)
}
endpointURL = &url.URL{
Scheme: client.baseURL.Scheme,
User: client.baseURL.User,
Host: client.baseURL.Host,
Path: endpointURL.Path,
RawPath: endpointURL.RawPath,
}
values := endpointURL.Query()
for key, value := range params {
values.Set(key, value)
}
endpointURL.RawQuery = values.Encode()
request, err := http.NewRequestWithContext(ctx, method, endpointURL.String(), body)
if err != nil {
return fmt.Errorf("%w instantiate a request: %v", ErrFailed, err)
}
response, err := client.httpClient.Do(request)
if err != nil {
return fmt.Errorf("%w to make a request: %v", ErrFailed, err)
}
defer func() {
_ = response.Body.Close()
}()
if response.StatusCode != http.StatusOK {
return fmt.Errorf("%w to make a request: %d %s",
ErrFailed, response.StatusCode, http.StatusText(response.StatusCode))
}
if out != nil {
bodyBytes, err := io.ReadAll(response.Body)
if err != nil {
return fmt.Errorf("%w to read response body: %v", ErrFailed, err)
}
if err := json.Unmarshal(bodyBytes, out); err != nil {
return fmt.Errorf("%w to unmarshal response body: %v", ErrFailed, err)
}
}
return nil
}
func (client *Client) Workers() *WorkersService {
return &WorkersService{
client: client,
}
}
func (client *Client) VMs() *VMsService {
return &VMsService{
client: client,
}
}