mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
197 lines
5.8 KiB
Go
197 lines
5.8 KiB
Go
package client
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/kolo/xmlrpc"
|
|
)
|
|
|
|
const (
|
|
// Production is the SystemType to provide to New to use the production XML API
|
|
Production SystemType = iota
|
|
// Testing is the SystemType to provide to New to use the test XML API
|
|
Testing
|
|
// LiveDNS is the SystemType to provide to New to use the Live DNS REST API
|
|
// Full documentation of the API is available here: http://doc.livedns.gandi.net/
|
|
LiveDNS
|
|
)
|
|
|
|
// SystemType is the type used to resolve gandi API address
|
|
type SystemType int
|
|
|
|
// Url returns the actual gandi API base URL
|
|
func (s SystemType) Url() string {
|
|
if s == Production {
|
|
return "https://rpc.gandi.net/xmlrpc/"
|
|
}
|
|
if s == LiveDNS {
|
|
return "https://dns.api.gandi.net/api/v5/"
|
|
}
|
|
return "https://rpc.ote.gandi.net/xmlrpc/"
|
|
}
|
|
|
|
// Client holds the configuration of a gandi client
|
|
type Client struct {
|
|
// Key is the API key to provide to gandi
|
|
Key string
|
|
// Url is the base URL of the gandi API
|
|
Url string
|
|
}
|
|
|
|
// New creates a new gandi client for the given system
|
|
func New(apiKey string, system SystemType) *Client {
|
|
return &Client{
|
|
Key: apiKey,
|
|
Url: system.Url(),
|
|
}
|
|
}
|
|
|
|
// Call performs an acual XML RPC call to the gandi API
|
|
func (c *Client) Call(serviceMethod string, args []interface{}, reply interface{}) error {
|
|
rpc, err := xmlrpc.NewClient(c.Url, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return rpc.Call(serviceMethod, args, reply)
|
|
}
|
|
|
|
// DoRest performs a request to gandi LiveDNS api and optionnally decodes the reply
|
|
func (c *Client) DoRest(req *http.Request, decoded interface{}) (*http.Response, error) {
|
|
if decoded != nil {
|
|
req.Header.Set("Accept", "application/json")
|
|
}
|
|
client := http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.StatusCode == http.StatusUnauthorized {
|
|
return nil, fmt.Errorf("the server returned unauthorized code. Your API key might be invalid or have expired")
|
|
}
|
|
//
|
|
defer func() { err = resp.Body.Close() }()
|
|
if decoded != nil {
|
|
b, e := ioutil.ReadAll(resp.Body)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
if len(b) > 0 {
|
|
e = json.Unmarshal(b, decoded)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
}
|
|
resp.Body = ioutil.NopCloser(bytes.NewReader(b))
|
|
}
|
|
return resp, err
|
|
}
|
|
|
|
// NewJSONRequest creates a new authenticated to gandi live DNS REST API.
|
|
// If data is not null, it will be encoded as json and prodived in the request body
|
|
func (c *Client) NewJSONRequest(method string, url string, data interface{}) (*http.Request, error) {
|
|
var reader io.Reader
|
|
if data != nil {
|
|
b, err := json.Marshal(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reader = bytes.NewReader(b)
|
|
}
|
|
req, err := http.NewRequest(method, fmt.Sprintf("%s/%s", strings.TrimRight(c.Url, "/"), strings.TrimLeft(url, "/")), reader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if data != nil {
|
|
req.Header.Set("Content-Type", "application/json")
|
|
}
|
|
req.Header.Set("X-Api-Key", c.Key)
|
|
return req, nil
|
|
}
|
|
|
|
// Get performs a Get request to gandi Live DNS api and decodes the returned data if a not null decoded pointer is provided
|
|
func (c *Client) Get(URI string, decoded interface{}) (*http.Response, error) {
|
|
req, err := c.NewJSONRequest("GET", URI, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp, err := c.DoRest(req, decoded)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("Unexpected http code %d on URL %v. expecting %d", resp.StatusCode, resp.Request.URL, http.StatusOK)
|
|
}
|
|
return resp, err
|
|
}
|
|
|
|
// Delete performs a Delete request to gandi Live DNS api and decodes the returned data if a not null decoded pointer is provided
|
|
func (c *Client) Delete(URI string, decoded interface{}) (*http.Response, error) {
|
|
req, err := c.NewJSONRequest("DELETE", URI, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp, err := c.DoRest(req, decoded)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.StatusCode != http.StatusNoContent {
|
|
return nil, fmt.Errorf("Unexpected http code %d on URL %v. expecting %d", resp.StatusCode, resp.Request.URL, http.StatusNoContent)
|
|
}
|
|
return resp, err
|
|
}
|
|
|
|
// Post performs a Post request request to gandi Live DNS api
|
|
// - with data encoded as JSON if a not null data pointer is provided
|
|
// - decodes the returned data if a not null decoded pointer is provided
|
|
// - ensures the status code is an HTTP accepted
|
|
func (c *Client) Post(URI string, data interface{}, decoded interface{}) (*http.Response, error) {
|
|
req, err := c.NewJSONRequest("POST", URI, data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp, err := c.DoRest(req, decoded)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.StatusCode != http.StatusCreated {
|
|
return nil, fmt.Errorf("Unexpected http code %d on URL %v. expecting %d", resp.StatusCode, resp.Request.URL, http.StatusCreated)
|
|
}
|
|
return resp, err
|
|
}
|
|
|
|
// Put performs a Put request to gandi Live DNS api
|
|
// - with data encoded as JSON if a not null data pointer is provided
|
|
// - decodes the returned data if a not null decoded pointer is provided
|
|
func (c *Client) Put(URI string, data interface{}, decoded interface{}) (*http.Response, error) {
|
|
req, err := c.NewJSONRequest("PUT", URI, data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c.DoRest(req, decoded)
|
|
}
|
|
|
|
// Patch performs a Patch request to gandi Live DNS api
|
|
// - with data encoded as JSON if a not null data pointer is provided
|
|
// - decodes the returned data if a not null decoded pointer is provided
|
|
// - ensures the status code is an HTTP accepted
|
|
func (c *Client) Patch(URI string, data interface{}, decoded interface{}) (*http.Response, error) {
|
|
req, err := c.NewJSONRequest("PATCH", URI, data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp, err := c.DoRest(req, decoded)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.StatusCode != http.StatusAccepted {
|
|
return nil, fmt.Errorf("Unexpected http code %d on URL %v. expecting %d", resp.StatusCode, resp.Request.URL, http.StatusAccepted)
|
|
}
|
|
return resp, err
|
|
}
|