File: client.go

package info (click to toggle)
golang-github-xenolf-lego 4.9.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,080 kB
  • sloc: xml: 533; makefile: 128; sh: 18
file content (147 lines) | stat: -rw-r--r-- 3,558 bytes parent folder | download | duplicates (2)
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
}