blob: 0af0c6501cf943a1f3ddd665dc8e70d5f4e3a427 [file] [log] [blame]
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httputil"
"time"
)
const (
GandiLiveDnsBaseUrl = "https://api.gandi.net/v5/livedns"
)
type GandiClient struct {
apiKey string
dumpRequestResponse bool
}
type GandiRRSet struct {
Type string `json:"rrset_type"`
TTL int `json:"rrset_ttl"`
Name string `json:"rrset_name"`
Values []string `json:"rrset_values"`
}
type GandiRRSetValues struct {
TTL int `json:"rrset_ttl"`
Values []string `json:"rrset_values"`
}
func NewGandiClient(apiKey string) *GandiClient {
return &GandiClient{
apiKey: apiKey,
dumpRequestResponse: false,
}
}
func (c *GandiClient) gandiRecordsUrl(domain string) string {
return fmt.Sprintf("%s/domains/%s/records", GandiLiveDnsBaseUrl, domain)
}
func (c *GandiClient) doRequest(req *http.Request, readResponseBody bool) (int, []byte, error) {
if c.dumpRequestResponse {
dump, _ := httputil.DumpRequest(req, true)
fmt.Printf("Request: %q\n", dump)
}
req.Header.Set("Authorization", fmt.Sprintf("Apikey %s", c.apiKey))
client := http.Client{
Timeout: 30 * time.Second,
}
res, err := client.Do(req)
if err != nil {
return 0, nil, err
}
if c.dumpRequestResponse {
dump, _ := httputil.DumpResponse(res, true)
fmt.Printf("Response: %q\n", dump)
}
if res.StatusCode == http.StatusOK && readResponseBody {
data, err := ioutil.ReadAll(res.Body)
if err != nil {
return 0, nil, err
}
return res.StatusCode, data, nil
}
return res.StatusCode, nil, nil
}
func (c *GandiClient) HasTxtRecord(domain *string, name *string) (bool, error) {
// curl -X GET -H "Content-Type: application/json" \
// -H "Authorization: Apikey $APIKEY" \
// https://api.gandi.net/v5/livedns/domains/<DOMAIN>/records/<NAME>/<TYPE>
url := fmt.Sprintf("%s/%s/TXT", c.gandiRecordsUrl(*domain), *name)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return false, err
}
status, _, err := c.doRequest(req, false)
if err != nil {
return false, err
}
if status == http.StatusNotFound {
return false, nil
} else if status == http.StatusOK {
// Maybe parse response body here to really ensure that the record is present
return true, nil
} else {
return false, fmt.Errorf("unexpected HTTP status: %d", status)
}
}
func (c *GandiClient) CreateTxtRecord(domain *string, name *string, value *string, ttl int) error {
// curl -X POST -H "Content-Type: application/json" \
// -H "Authorization: Apikey $APIKEY" \
// -d '{"rrset_name": "<NAME>", "rrset_type": "<TYPE>", "rrset_ttl": 10800, "rrset_values": ["<VALUE>"]}' \
// https://api.gandi.net/v5/livedns/domains/<DOMAIN>/records
rrs := GandiRRSet{Name: *name, Type: "TXT", TTL: ttl, Values: []string{*value}}
body, err := json.Marshal(rrs)
if err != nil {
return fmt.Errorf("cannot marshall to json: %v", err)
}
url := c.gandiRecordsUrl(*domain)
req, err := http.NewRequest("POST", url, bytes.NewReader(body))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
status, _, err := c.doRequest(req, false)
if err != nil {
return err
}
if status != http.StatusCreated && status != http.StatusOK {
return fmt.Errorf("failed creating TXT record: %v", err)
}
return nil
}
func (c *GandiClient) UpdateTxtRecord(domain *string, name *string, value *string, ttl int) error {
// curl -X PUT -H "Content-Type: application/json" \
// -H "Authorization: Apikey $APIKEY" \
// -d '{"rrset_ttl": 10800, "rrset_values":["<VALUE>"]}' \
// https://api.gandi.net/v5/livedns/domains/<DOMAIN>/records/<NAME>/<TYPE>
rrs := GandiRRSetValues{TTL: ttl, Values: []string{*value}}
body, err := json.Marshal(rrs)
if err != nil {
return fmt.Errorf("cannot marshall to json: %v", err)
}
url := fmt.Sprintf("%s/%s/TXT", c.gandiRecordsUrl(*domain), *name)
req, err := http.NewRequest("PUT", url, bytes.NewReader(body))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
status, _, err := c.doRequest(req, false)
if err != nil {
return err
}
if status != http.StatusCreated && status != http.StatusOK {
return fmt.Errorf("failed updating TXT record: %v", err)
}
return nil
}
func (c *GandiClient) DeleteTxtRecord(domain *string, name *string) error {
// curl -X DELETE -H "Content-Type: application/json" \
// -H "Authorization: Apikey $APIKEY" \
// https://api.gandi.net/v5/livedns/domains/<DOMAIN>/records/<NAME>/<TYPE>
url := fmt.Sprintf("%s/%s/TXT", c.gandiRecordsUrl(*domain), *name)
req, err := http.NewRequest("DELETE", url, nil)
if err != nil {
return err
}
status, _, err := c.doRequest(req, false)
if err != nil {
return err
}
if status != http.StatusOK && status != http.StatusNoContent {
return fmt.Errorf("failed deleting TXT record: %v", err)
}
return nil
}