client.go raw

   1  // Package v3 provides primitives to interact with the openapi HTTP API.
   2  //
   3  // Code generated by github.com/egoscale/v3/generator version v0.0.1 DO NOT EDIT.
   4  package v3
   5  
   6  import (
   7  	"context"
   8  	"fmt"
   9  	"io"
  10  	"log"
  11  	"net/http"
  12  	"runtime"
  13  	"time"
  14  
  15  	"github.com/exoscale/egoscale/v3/credentials"
  16  	"github.com/go-playground/validator/v10"
  17  	"github.com/hashicorp/go-retryablehttp"
  18  )
  19  
  20  // Endpoint represents a zone endpoint.
  21  type Endpoint string
  22  
  23  const (
  24  	CHGva2 Endpoint = "https://api-ch-gva-2.exoscale.com/v2"
  25  	CHDk2  Endpoint = "https://api-ch-dk-2.exoscale.com/v2"
  26  	DEFra1 Endpoint = "https://api-de-fra-1.exoscale.com/v2"
  27  	DEMuc1 Endpoint = "https://api-de-muc-1.exoscale.com/v2"
  28  	ATVie1 Endpoint = "https://api-at-vie-1.exoscale.com/v2"
  29  	ATVie2 Endpoint = "https://api-at-vie-2.exoscale.com/v2"
  30  	BGSof1 Endpoint = "https://api-bg-sof-1.exoscale.com/v2"
  31  	HrZag1 Endpoint = "https://api-hr-zag-1.exoscale.com/v2"
  32  )
  33  
  34  // defaultHTTPClient is HTTP client with retry logic.
  35  // Default retry configuration can be found in go-retryablehttp repo.
  36  var defaultHTTPClient = func() *http.Client {
  37  	rc := retryablehttp.NewClient()
  38  	// silence client by default
  39  	rc.Logger = log.New(io.Discard, "", 0)
  40  	return rc.StandardClient()
  41  }()
  42  
  43  func (c Client) GetZoneName(ctx context.Context, endpoint Endpoint) (ZoneName, error) {
  44  	resp, err := c.ListZones(ctx)
  45  	if err != nil {
  46  		return "", fmt.Errorf("get zone name: list zones: %w", err)
  47  	}
  48  
  49  	zone, err := resp.FindZone(string(endpoint))
  50  	if err != nil {
  51  		return "", fmt.Errorf("get zone name: find zone: %w", err)
  52  	}
  53  
  54  	return zone.Name, nil
  55  }
  56  
  57  func (c Client) GetZoneAPIEndpoint(ctx context.Context, zoneName ZoneName) (Endpoint, error) {
  58  	resp, err := c.ListZones(ctx)
  59  	if err != nil {
  60  		return "", fmt.Errorf("get zone api endpoint: list zones: %w", err)
  61  	}
  62  
  63  	zone, err := resp.FindZone(string(zoneName))
  64  	if err != nil {
  65  		return "", fmt.Errorf("get zone api endpoint: find zone: %w", err)
  66  	}
  67  
  68  	return zone.APIEndpoint, nil
  69  }
  70  
  71  // Client represents an Exoscale API client.
  72  type Client struct {
  73  	apiKey         string
  74  	apiSecret      string
  75  	userAgent      string
  76  	serverEndpoint string
  77  	httpClient     *http.Client
  78  	waitTimeout    time.Duration
  79  	validate       *validator.Validate
  80  	trace          bool
  81  
  82  	// A list of callbacks for modifying requests which are generated before sending over
  83  	// the network.
  84  	requestInterceptors []RequestInterceptorFn
  85  }
  86  
  87  // RequestInterceptorFn is the function signature for the RequestInterceptor callback function
  88  type RequestInterceptorFn func(ctx context.Context, req *http.Request) error
  89  
  90  // Deprecated: use ClientOptWithUserAgent instead.
  91  var UserAgent = getDefaultUserAgent()
  92  
  93  // ClientOpt represents a function setting Exoscale API client option.
  94  type ClientOpt func(*Client) error
  95  
  96  // ClientOptWithTrace returns a ClientOpt enabling HTTP request/response tracing.
  97  func ClientOptWithTrace() ClientOpt {
  98  	return func(c *Client) error {
  99  		c.trace = true
 100  		return nil
 101  	}
 102  }
 103  
 104  // ClientOptWithUserAgent returns a ClientOpt setting the user agent header.
 105  func ClientOptWithUserAgent(ua string) ClientOpt {
 106  	return func(c *Client) error {
 107  		c.userAgent = ua + " " + getDefaultUserAgent()
 108  		return nil
 109  	}
 110  }
 111  
 112  // ClientOptWithValidator returns a ClientOpt with a given validator.
 113  func ClientOptWithValidator(validate *validator.Validate) ClientOpt {
 114  	return func(c *Client) error {
 115  		c.validate = validate
 116  		return nil
 117  	}
 118  }
 119  
 120  // ClientOptWithEndpoint returns a ClientOpt With a given zone Endpoint.
 121  func ClientOptWithEndpoint(endpoint Endpoint) ClientOpt {
 122  	return func(c *Client) error {
 123  		c.serverEndpoint = string(endpoint)
 124  		return nil
 125  	}
 126  }
 127  
 128  // ClientOptWithWaitTimeout returns a ClientOpt With a given wait timeout.
 129  func ClientOptWithWaitTimeout(t time.Duration) ClientOpt {
 130  	return func(c *Client) error {
 131  		c.waitTimeout = t
 132  		return nil
 133  	}
 134  }
 135  
 136  // ClientOptWithRequestInterceptors returns a ClientOpt With given RequestInterceptors.
 137  func ClientOptWithRequestInterceptors(f ...RequestInterceptorFn) ClientOpt {
 138  	return func(c *Client) error {
 139  		c.requestInterceptors = append(c.requestInterceptors, f...)
 140  		return nil
 141  	}
 142  }
 143  
 144  // ClientOptWithHTTPClient returns a ClientOpt overriding the default http.Client.
 145  // Note: the Exoscale API client will chain additional middleware
 146  // (http.RoundTripper) on the HTTP client internally, which can alter the HTTP
 147  // requests and responses. If you don't want any other middleware than the ones
 148  // currently set to your HTTP client, you should duplicate it and pass a copy
 149  // instead.
 150  func ClientOptWithHTTPClient(v *http.Client) ClientOpt {
 151  	return func(c *Client) error {
 152  		c.httpClient = v
 153  
 154  		return nil
 155  	}
 156  }
 157  
 158  // getDefaultUserAgent returns the "User-Agent" HTTP request header added to outgoing HTTP requests.
 159  func getDefaultUserAgent() string {
 160  	return fmt.Sprintf("egoscale/%s (%s; %s/%s)",
 161  		Version,
 162  		runtime.Version(),
 163  		runtime.GOOS,
 164  		runtime.GOARCH)
 165  }
 166  
 167  // NewClient returns a new Exoscale API client.
 168  func NewClient(credentials *credentials.Credentials, opts ...ClientOpt) (*Client, error) {
 169  	values, err := credentials.Get()
 170  	if err != nil {
 171  		return nil, err
 172  	}
 173  
 174  	client := &Client{
 175  		apiKey:         values.APIKey,
 176  		apiSecret:      values.APISecret,
 177  		serverEndpoint: string(CHGva2),
 178  		httpClient:     defaultHTTPClient,
 179  		validate:       validator.New(),
 180  		userAgent:      getDefaultUserAgent(),
 181  	}
 182  
 183  	for _, opt := range opts {
 184  		if err := opt(client); err != nil {
 185  			return nil, fmt.Errorf("client configuration error: %s", err)
 186  		}
 187  	}
 188  
 189  	return client, nil
 190  }
 191  
 192  // getUserAgent only for compatibility with UserAgent.
 193  func (c *Client) getUserAgent() string {
 194  	defaultUA := getDefaultUserAgent()
 195  
 196  	if c.userAgent != defaultUA {
 197  		return c.userAgent
 198  	}
 199  
 200  	if UserAgent != defaultUA {
 201  		return UserAgent
 202  	}
 203  
 204  	return c.userAgent
 205  }
 206  
 207  // WithEndpoint returns a copy of Client with new zone Endpoint.
 208  func (c *Client) WithEndpoint(endpoint Endpoint) *Client {
 209  	clone := cloneClient(c)
 210  
 211  	clone.serverEndpoint = string(endpoint)
 212  
 213  	return clone
 214  }
 215  
 216  // WithWaitTimeout returns a copy of Client with new wait timeout.
 217  func (c *Client) WithWaitTimeout(t time.Duration) *Client {
 218  	clone := cloneClient(c)
 219  
 220  	clone.waitTimeout = t
 221  
 222  	return clone
 223  }
 224  
 225  // WithUserAgent returns a copy of Client with new User-Agent.
 226  func (c *Client) WithUserAgent(ua string) *Client {
 227  	clone := cloneClient(c)
 228  
 229  	clone.userAgent = ua + " " + getDefaultUserAgent()
 230  
 231  	return clone
 232  }
 233  
 234  // WithTrace returns a copy of Client with tracing enabled.
 235  func (c *Client) WithTrace() *Client {
 236  	clone := cloneClient(c)
 237  
 238  	clone.trace = true
 239  
 240  	return clone
 241  }
 242  
 243  // WithHttpClient returns a copy of Client with new http.Client.
 244  // Deprecated: use WithHTTPClient instead.
 245  func (c *Client) WithHttpClient(client *http.Client) *Client {
 246  	clone := cloneClient(c)
 247  
 248  	clone.httpClient = client
 249  
 250  	return clone
 251  }
 252  
 253  // WithHTTPClient returns a copy of Client with new http.Client.
 254  func (c *Client) WithHTTPClient(client *http.Client) *Client {
 255  	clone := cloneClient(c)
 256  
 257  	clone.httpClient = client
 258  
 259  	return clone
 260  }
 261  
 262  // WithRequestInterceptor returns a copy of Client with new RequestInterceptors.
 263  func (c *Client) WithRequestInterceptor(f ...RequestInterceptorFn) *Client {
 264  	clone := cloneClient(c)
 265  
 266  	clone.requestInterceptors = append(clone.requestInterceptors, f...)
 267  
 268  	return clone
 269  }
 270  
 271  func (c *Client) executeRequestInterceptors(ctx context.Context, req *http.Request) error {
 272  	for _, fn := range c.requestInterceptors {
 273  		if err := fn(ctx, req); err != nil {
 274  			return err
 275  		}
 276  	}
 277  
 278  	return nil
 279  }
 280  
 281  func cloneClient(c *Client) *Client {
 282  	return &Client{
 283  		apiKey:              c.apiKey,
 284  		apiSecret:           c.apiSecret,
 285  		userAgent:           c.userAgent,
 286  		serverEndpoint:      c.serverEndpoint,
 287  		httpClient:          c.httpClient,
 288  		requestInterceptors: c.requestInterceptors,
 289  		waitTimeout:         c.waitTimeout,
 290  		trace:               c.trace,
 291  		validate:            c.validate,
 292  	}
 293  }
 294