1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
package dyn
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
)
const defaultBaseURL = "https://api.dynect.net/REST"
type dynResponse struct {
// One of 'success', 'failure', or 'incomplete'
Status string `json:"status"`
// The structure containing the actual results of the request
Data json.RawMessage `json:"data"`
// The ID of the job that was created in response to a request.
JobID int `json:"job_id"`
// A list of zero or more messages
Messages json.RawMessage `json:"msgs"`
}
type credentials struct {
Customer string `json:"customer_name"`
User string `json:"user_name"`
Pass string `json:"password"`
}
type session struct {
Token string `json:"token"`
Version string `json:"version"`
}
type publish struct {
Publish bool `json:"publish"`
Notes string `json:"notes"`
}
// Starts a new Dyn API Session. Authenticates using customerName, userName,
// password and receives a token to be used in for subsequent requests.
func (d *DNSProvider) login() error {
payload := &credentials{Customer: d.config.CustomerName, User: d.config.UserName, Pass: d.config.Password}
dynRes, err := d.sendRequest(http.MethodPost, "Session", payload)
if err != nil {
return err
}
var s session
err = json.Unmarshal(dynRes.Data, &s)
if err != nil {
return err
}
d.token = s.Token
return nil
}
// Destroys Dyn Session.
func (d *DNSProvider) logout() error {
if d.token == "" {
// nothing to do
return nil
}
url := fmt.Sprintf("%s/Session", defaultBaseURL)
req, err := http.NewRequest(http.MethodDelete, url, nil)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Auth-Token", d.token)
resp, err := d.config.HTTPClient.Do(req)
if err != nil {
return err
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("API request failed to delete session with HTTP status code %d", resp.StatusCode)
}
d.token = ""
return nil
}
func (d *DNSProvider) publish(zone, notes string) error {
pub := &publish{Publish: true, Notes: notes}
resource := fmt.Sprintf("Zone/%s/", zone)
_, err := d.sendRequest(http.MethodPut, resource, pub)
return err
}
func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) (*dynResponse, error) {
url := fmt.Sprintf("%s/%s", defaultBaseURL, resource)
body, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequest(method, url, bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
if len(d.token) > 0 {
req.Header.Set("Auth-Token", d.token)
}
resp, err := d.config.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode >= http.StatusInternalServerError {
return nil, fmt.Errorf("API request failed with HTTP status code %d", resp.StatusCode)
}
var dynRes dynResponse
err = json.NewDecoder(resp.Body).Decode(&dynRes)
if err != nil {
return nil, err
}
if resp.StatusCode >= http.StatusBadRequest {
return nil, fmt.Errorf("API request failed with HTTP status code %d: %s", resp.StatusCode, dynRes.Messages)
} else if resp.StatusCode == http.StatusTemporaryRedirect {
// TODO add support for HTTP 307 response and long running jobs
return nil, errors.New("API request returned HTTP 307. This is currently unsupported")
}
if dynRes.Status == "failure" {
// TODO add better error handling
return nil, fmt.Errorf("API request failed: %s", dynRes.Messages)
}
return &dynRes, nil
}
|