Add DNS Provider for VegaDNS (#553)

This commit is contained in:
Bill Shupp 2018-06-30 16:45:24 -07:00 committed by Ludovic Fernandez
parent 9bb5589e17
commit 3f09ecc0b1
11 changed files with 857 additions and 1 deletions

8
Gopkg.lock generated
View file

@ -34,6 +34,12 @@
revision = "fa1c0367800db75e4d10d0ec90c49a8731670224" revision = "fa1c0367800db75e4d10d0ec90c49a8731670224"
version = "1.15.0" version = "1.15.0"
[[projects]]
branch = "master"
name = "github.com/OpenDNS/vegadns2client"
packages = ["."]
revision = "a3fa4a771d87bda2514a90a157e1fed1b6897d2e"
[[projects]] [[projects]]
name = "github.com/akamai/AkamaiOPEN-edgegrid-golang" name = "github.com/akamai/AkamaiOPEN-edgegrid-golang"
packages = [ packages = [
@ -374,6 +380,6 @@
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "d93e6f0825f1f7c1c3c2ccd421af4d99b77b390f3346ca751968af0609119c96" inputs-digest = "68128f1cf61f649cdac1483e18c54541782cbd344605cab3cf6c2b448a86bda0"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

1
cli.go
View file

@ -221,6 +221,7 @@ Here is an example bash command using the CloudFlare DNS provider:
fmt.Fprintln(w, "\trfc2136:\tRFC2136_TSIG_KEY, RFC2136_TSIG_SECRET,\n\t\tRFC2136_TSIG_ALGORITHM, RFC2136_NAMESERVER") fmt.Fprintln(w, "\trfc2136:\tRFC2136_TSIG_KEY, RFC2136_TSIG_SECRET,\n\t\tRFC2136_TSIG_ALGORITHM, RFC2136_NAMESERVER")
fmt.Fprintln(w, "\troute53:\tAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, AWS_HOSTED_ZONE_ID") fmt.Fprintln(w, "\troute53:\tAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, AWS_HOSTED_ZONE_ID")
fmt.Fprintln(w, "\tdyn:\tDYN_CUSTOMER_NAME, DYN_USER_NAME, DYN_PASSWORD") fmt.Fprintln(w, "\tdyn:\tDYN_CUSTOMER_NAME, DYN_USER_NAME, DYN_PASSWORD")
fmt.Fprintln(w, "\tvegadns:\tSECRET_VEGADNS_KEY, SECRET_VEGADNS_SECRET, VEGADNS_URL")
fmt.Fprintln(w, "\tvultr:\tVULTR_API_KEY") fmt.Fprintln(w, "\tvultr:\tVULTR_API_KEY")
fmt.Fprintln(w, "\tovh:\tOVH_ENDPOINT, OVH_APPLICATION_KEY, OVH_APPLICATION_SECRET, OVH_CONSUMER_KEY") fmt.Fprintln(w, "\tovh:\tOVH_ENDPOINT, OVH_APPLICATION_KEY, OVH_APPLICATION_SECRET, OVH_CONSUMER_KEY")
fmt.Fprintln(w, "\tpdns:\tPDNS_API_KEY, PDNS_API_URL") fmt.Fprintln(w, "\tpdns:\tPDNS_API_KEY, PDNS_API_URL")

View file

@ -35,6 +35,7 @@ import (
"github.com/xenolf/lego/providers/dns/rfc2136" "github.com/xenolf/lego/providers/dns/rfc2136"
"github.com/xenolf/lego/providers/dns/route53" "github.com/xenolf/lego/providers/dns/route53"
"github.com/xenolf/lego/providers/dns/sakuracloud" "github.com/xenolf/lego/providers/dns/sakuracloud"
"github.com/xenolf/lego/providers/dns/vegadns"
"github.com/xenolf/lego/providers/dns/vultr" "github.com/xenolf/lego/providers/dns/vultr"
) )
@ -107,6 +108,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
return otc.NewDNSProvider() return otc.NewDNSProvider()
case "exec": case "exec":
return exec.NewDNSProvider() return exec.NewDNSProvider()
case "vegadns":
return vegadns.NewDNSProvider()
default: default:
return nil, fmt.Errorf("unrecognised DNS provider: %s", name) return nil, fmt.Errorf("unrecognised DNS provider: %s", name)
} }

View file

@ -0,0 +1,85 @@
// Package vegadns implements a DNS provider for solving the DNS-01
// challenge using VegaDNS.
package vegadns
import (
"fmt"
"os"
"strings"
"time"
vegaClient "github.com/OpenDNS/vegadns2client"
"github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
)
// DNSProvider describes a provider for VegaDNS
type DNSProvider struct {
client vegaClient.VegaDNSClient
}
// NewDNSProvider returns a DNSProvider instance configured for VegaDNS.
// Credentials must be passed in the environment variables:
// VEGADNS_URL, SECRET_VEGADNS_KEY, SECRET_VEGADNS_SECRET.
func NewDNSProvider() (*DNSProvider, error) {
values, err := env.Get("VEGADNS_URL")
if err != nil {
return nil, fmt.Errorf("VegaDNS: %v", err)
}
key := os.Getenv("SECRET_VEGADNS_KEY")
secret := os.Getenv("SECRET_VEGADNS_SECRET")
return NewDNSProviderCredentials(values["VEGADNS_URL"], key, secret)
}
// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for VegaDNS.
func NewDNSProviderCredentials(vegaDNSURL string, key string, secret string) (*DNSProvider, error) {
vega := vegaClient.NewVegaDNSClient(vegaDNSURL)
vega.APIKey = key
vega.APISecret = secret
return &DNSProvider{
client: vega,
}, nil
}
// Timeout returns the timeout and interval to use when checking for DNS
// propagation. Adjusting here to cope with spikes in propagation times.
func (r *DNSProvider) Timeout() (timeout, interval time.Duration) {
timeout = 12 * time.Minute
interval = 1 * time.Minute
return
}
// Present creates a TXT record to fulfil the dns-01 challenge
func (r *DNSProvider) Present(domain, token, keyAuth string) error {
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
_, domainID, err := r.client.GetAuthZone(fqdn)
if err != nil {
return fmt.Errorf("can't find Authoritative Zone for %s in Present: %v", fqdn, err)
}
return r.client.CreateTXT(domainID, fqdn, value, 10)
}
// CleanUp removes the TXT record matching the specified parameters
func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
_, domainID, err := r.client.GetAuthZone(fqdn)
if err != nil {
return fmt.Errorf("can't find Authoritative Zone for %s in CleanUp: %v", fqdn, err)
}
txt := strings.TrimSuffix(fqdn, ".")
recordID, err := r.client.GetRecordID(domainID, txt, "TXT")
if err != nil {
return fmt.Errorf("couldn't get Record ID in CleanUp: %s", err)
}
return r.client.DeleteRecord(recordID)
}

View file

@ -0,0 +1,391 @@
// Package vegadns implements a DNS provider for solving the DNS-01
// challenge using VegaDNS.
package vegadns
import (
"fmt"
"net"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var ipPort = "127.0.0.1:2112"
var jsonMap = map[string]string{
"token": `
{
"access_token":"699dd4ff-e381-46b8-8bf8-5de49dd56c1f",
"token_type":"bearer",
"expires_in":3600
}
`,
"domains": `
{
"domains":[
{
"domain_id":1,
"domain":"example.com",
"status":"active",
"owner_id":0
}
]
}
`,
"records": `
{
"status":"ok",
"total_records":2,
"domain":{
"status":"active",
"domain":"example.com",
"owner_id":0,
"domain_id":1
},
"records":[
{
"retry":"2048",
"minimum":"2560",
"refresh":"16384",
"email":"hostmaster.example.com",
"record_type":"SOA",
"expire":"1048576",
"ttl":86400,
"record_id":1,
"nameserver":"ns1.example.com",
"domain_id":1,
"serial":""
},
{
"name":"example.com",
"value":"ns1.example.com",
"record_type":"NS",
"ttl":3600,
"record_id":2,
"location_id":null,
"domain_id":1
},
{
"name":"_acme-challenge.example.com",
"value":"my_challenge",
"record_type":"TXT",
"ttl":3600,
"record_id":3,
"location_id":null,
"domain_id":1
}
]
}
`,
"recordCreated": `
{
"status":"ok",
"record":{
"name":"_acme-challenge.example.com",
"value":"my_challenge",
"record_type":"TXT",
"ttl":3600,
"record_id":3,
"location_id":null,
"domain_id":1
}
}
`,
"recordDeleted": `{"status": "ok"}`,
}
type muxCallback func() *http.ServeMux
func TestVegaDNSNewDNSProviderFail(t *testing.T) {
os.Setenv("VEGADNS_URL", "")
_, err := NewDNSProvider()
assert.Error(t, err, "VEGADNS_URL env missing")
}
func TestVegaDNSTimeoutSuccess(t *testing.T) {
ts, err := startTestServer(vegaDNSMuxSuccess)
require.NoError(t, err)
defer ts.Close()
defer os.Clearenv()
provider, err := NewDNSProvider()
require.NoError(t, err)
timeout, interval := provider.Timeout()
assert.Equal(t, timeout, time.Duration(720000000000))
assert.Equal(t, interval, time.Duration(60000000000))
}
func TestVegaDNSPresentSuccess(t *testing.T) {
ts, err := startTestServer(vegaDNSMuxSuccess)
require.NoError(t, err)
defer ts.Close()
defer os.Clearenv()
provider, err := NewDNSProvider()
require.NoError(t, err)
err = provider.Present("example.com", "token", "keyAuth")
assert.NoError(t, err)
}
func TestVegaDNSPresentFailToFindZone(t *testing.T) {
ts, err := startTestServer(vegaDNSMuxFailToFindZone)
require.NoError(t, err)
defer ts.Close()
defer os.Clearenv()
provider, err := NewDNSProvider()
require.NoError(t, err)
err = provider.Present("example.com", "token", "keyAuth")
assert.EqualError(t, err, "can't find Authoritative Zone for _acme-challenge.example.com. in Present: Unable to find auth zone for fqdn _acme-challenge.example.com")
}
func TestVegaDNSPresentFailToCreateTXT(t *testing.T) {
ts, err := startTestServer(vegaDNSMuxFailToCreateTXT)
require.NoError(t, err)
defer ts.Close()
defer os.Clearenv()
provider, err := NewDNSProvider()
require.NoError(t, err)
err = provider.Present("example.com", "token", "keyAuth")
assert.EqualError(t, err, "Got bad answer from VegaDNS on CreateTXT. Code: 400. Message: ")
}
func TestVegaDNSCleanUpSuccess(t *testing.T) {
ts, err := startTestServer(vegaDNSMuxSuccess)
require.NoError(t, err)
defer ts.Close()
defer os.Clearenv()
provider, err := NewDNSProvider()
require.NoError(t, err)
err = provider.CleanUp("example.com", "token", "keyAuth")
assert.NoError(t, err)
}
func TestVegaDNSCleanUpFailToFindZone(t *testing.T) {
ts, err := startTestServer(vegaDNSMuxFailToFindZone)
require.NoError(t, err)
defer ts.Close()
defer os.Clearenv()
provider, err := NewDNSProvider()
require.NoError(t, err)
err = provider.CleanUp("example.com", "token", "keyAuth")
assert.EqualError(t, err, "can't find Authoritative Zone for _acme-challenge.example.com. in CleanUp: Unable to find auth zone for fqdn _acme-challenge.example.com")
}
func TestVegaDNSCleanUpFailToGetRecordID(t *testing.T) {
ts, err := startTestServer(vegaDNSMuxFailToGetRecordID)
require.NoError(t, err)
defer ts.Close()
defer os.Clearenv()
provider, err := NewDNSProvider()
require.NoError(t, err)
err = provider.CleanUp("example.com", "token", "keyAuth")
assert.EqualError(t, err, "couldn't get Record ID in CleanUp: Got bad answer from VegaDNS on GetRecordID. Code: 404. Message: ")
}
func vegaDNSMuxSuccess() *http.ServeMux {
mux := http.NewServeMux()
mux.HandleFunc("/1.0/token", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, jsonMap["token"])
return
}
w.WriteHeader(http.StatusBadRequest)
})
mux.HandleFunc("/1.0/domains", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("search") == "example.com" {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, jsonMap["domains"])
return
}
w.WriteHeader(http.StatusNotFound)
})
mux.HandleFunc("/1.0/records", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
if r.URL.Query().Get("domain_id") == "1" {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, jsonMap["records"])
return
}
w.WriteHeader(http.StatusNotFound)
return
case http.MethodPost:
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, jsonMap["recordCreated"])
return
}
w.WriteHeader(http.StatusBadRequest)
})
mux.HandleFunc("/1.0/records/3", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodDelete:
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, jsonMap["recordDeleted"])
return
}
w.WriteHeader(http.StatusBadRequest)
})
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
fmt.Printf("Not Found for Request: (%+v)\n\n", r)
})
return mux
}
func vegaDNSMuxFailToFindZone() *http.ServeMux {
mux := http.NewServeMux()
mux.HandleFunc("/1.0/token", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, jsonMap["token"])
return
}
w.WriteHeader(http.StatusBadRequest)
})
mux.HandleFunc("/1.0/domains", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
})
return mux
}
func vegaDNSMuxFailToCreateTXT() *http.ServeMux {
mux := http.NewServeMux()
mux.HandleFunc("/1.0/token", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, jsonMap["token"])
return
}
w.WriteHeader(http.StatusBadRequest)
})
mux.HandleFunc("/1.0/domains", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("search") == "example.com" {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, jsonMap["domains"])
return
}
w.WriteHeader(http.StatusNotFound)
})
mux.HandleFunc("/1.0/records", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
if r.URL.Query().Get("domain_id") == "1" {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, jsonMap["records"])
return
}
w.WriteHeader(http.StatusNotFound)
return
case http.MethodPost:
w.WriteHeader(http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusBadRequest)
})
return mux
}
func vegaDNSMuxFailToGetRecordID() *http.ServeMux {
mux := http.NewServeMux()
mux.HandleFunc("/1.0/token", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, jsonMap["token"])
return
}
w.WriteHeader(http.StatusBadRequest)
})
mux.HandleFunc("/1.0/domains", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("search") == "example.com" {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, jsonMap["domains"])
return
}
w.WriteHeader(http.StatusNotFound)
})
mux.HandleFunc("/1.0/records", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
w.WriteHeader(http.StatusNotFound)
return
}
w.WriteHeader(http.StatusBadRequest)
})
return mux
}
// Starts and returns a test server using a custom ip/port. Defer close() afterwards.
func startTestServer(callback muxCallback) (*httptest.Server, error) {
err := os.Setenv("SECRET_VEGADNS_KEY", "key")
if err != nil {
return nil, err
}
err = os.Setenv("SECRET_VEGADNS_SECRET", "secret")
if err != nil {
return nil, err
}
err = os.Setenv("VEGADNS_URL", "http://"+ipPort)
if err != nil {
return nil, err
}
ts := httptest.NewUnstartedServer(callback())
l, err := net.Listen("tcp", ipPort)
if err != nil {
return nil, err
}
ts.Listener = l
ts.Start()
return ts, nil
}

13
vendor/github.com/OpenDNS/vegadns2client/LICENSE generated vendored Normal file
View file

@ -0,0 +1,13 @@
Copyright 2018, Cisco Systems, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

71
vendor/github.com/OpenDNS/vegadns2client/client.go generated vendored Normal file
View file

@ -0,0 +1,71 @@
package vegadns2client
import (
"fmt"
"net/http"
"net/url"
"strings"
)
// VegaDNSClient - Struct for holding VegaDNSClient specific attributes
type VegaDNSClient struct {
client http.Client
baseurl string
version string
User string
Pass string
APIKey string
APISecret string
token Token
}
// Send - Central place for sending requests
// Input: method, endpoint, params
// Output: *http.Response
func (vega *VegaDNSClient) Send(method string, endpoint string, params map[string]string) (*http.Response, error) {
vegaURL := vega.getURL(endpoint)
p := url.Values{}
for k, v := range params {
p.Set(k, v)
}
var err error
var req *http.Request
if (method == "GET") || (method == "DELETE") {
vegaURL = fmt.Sprintf("%s?%s", vegaURL, p.Encode())
req, err = http.NewRequest(method, vegaURL, nil)
} else {
req, err = http.NewRequest(method, vegaURL, strings.NewReader(p.Encode()))
}
if err != nil {
return nil, fmt.Errorf("Error preparing request: %s", err)
}
if vega.User != "" && vega.Pass != "" {
// Basic Auth
req.SetBasicAuth(vega.User, vega.Pass)
} else if vega.APIKey != "" && vega.APISecret != "" {
// OAuth
vega.getAuthToken()
err = vega.token.valid()
if err != nil {
return nil, err
}
req.Header.Set("Authorization", vega.getBearer())
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
return vega.client.Do(req)
}
func (vega *VegaDNSClient) getURL(endpoint string) string {
return fmt.Sprintf("%s/%s/%s", vega.baseurl, vega.version, endpoint)
}
func (vega *VegaDNSClient) stillAuthorized() error {
return vega.token.valid()
}

80
vendor/github.com/OpenDNS/vegadns2client/domains.go generated vendored Normal file
View file

@ -0,0 +1,80 @@
package vegadns2client
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
)
// Domain - struct containing a domain object
type Domain struct {
Status string `json:"status"`
Domain string `json:"domain"`
DomainID int `json:"domain_id"`
OwnerID int `json:"owner_id"`
}
// DomainResponse - api response of a domain list
type DomainResponse struct {
Status string `json:"status"`
Total int `json:"total_domains"`
Domains []Domain `json:"domains"`
}
// GetDomainID - returns the id for a domain
// Input: domain
// Output: int, err
func (vega *VegaDNSClient) GetDomainID(domain string) (int, error) {
params := make(map[string]string)
params["search"] = domain
resp, err := vega.Send("GET", "domains", params)
if err != nil {
return -1, fmt.Errorf("Error sending GET to GetDomainID: %s", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return -1, fmt.Errorf("Error reading response from GET to GetDomainID: %s", err)
}
if resp.StatusCode != http.StatusOK {
return -1, fmt.Errorf("Got bad answer from VegaDNS on GetDomainID. Code: %d. Message: %s", resp.StatusCode, string(body))
}
answer := DomainResponse{}
if err := json.Unmarshal(body, &answer); err != nil {
return -1, fmt.Errorf("Error unmarshalling body from GetDomainID: %s", err)
}
log.Println(answer)
for _, d := range answer.Domains {
if d.Domain == domain {
return d.DomainID, nil
}
}
return -1, fmt.Errorf("Didnt find domain %s", domain)
}
// GetAuthZone retrieves the closest match to a given
// domain. Example: Given an argument "a.b.c.d.e", and a VegaDNS
// hosted domain of "c.d.e", GetClosestMatchingDomain will return
// "c.d.e".
func (vega *VegaDNSClient) GetAuthZone(fqdn string) (string, int, error) {
fqdn = strings.TrimSuffix(fqdn, ".")
numComponents := len(strings.Split(fqdn, "."))
for i := 1; i < numComponents; i++ {
tmpHostname := strings.SplitN(fqdn, ".", i)[i-1]
log.Printf("tmpHostname for i = %d: %s\n", i, tmpHostname)
if domainID, err := vega.GetDomainID(tmpHostname); err == nil {
log.Printf("Found zone: %s\n\tShortened to %s\n", tmpHostname, strings.TrimSuffix(tmpHostname, "."))
return strings.TrimSuffix(tmpHostname, "."), domainID, nil
}
}
log.Println("Unable to find hosted zone in vegadns")
return "", -1, fmt.Errorf("Unable to find auth zone for fqdn %s", fqdn)
}

19
vendor/github.com/OpenDNS/vegadns2client/main.go generated vendored Normal file
View file

@ -0,0 +1,19 @@
package vegadns2client
import (
"net/http"
"time"
)
// NewVegaDNSClient - helper to instantiate a client
// Input: url string
// Output: VegaDNSClient
func NewVegaDNSClient(url string) VegaDNSClient {
httpClient := http.Client{Timeout: 15 * time.Second}
return VegaDNSClient{
client: httpClient,
baseurl: url,
version: "1.0",
token: Token{},
}
}

113
vendor/github.com/OpenDNS/vegadns2client/records.go generated vendored Normal file
View file

@ -0,0 +1,113 @@
package vegadns2client
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"strings"
)
// Record - struct representing a Record object
type Record struct {
Name string `json:"name"`
Value string `json:"value"`
RecordType string `json:"record_type"`
TTL int `json:"ttl"`
RecordID int `json:"record_id"`
LocationID string `json:"location_id"`
DomainID int `json:"domain_id"`
}
// RecordsResponse - api response list of records
type RecordsResponse struct {
Status string `json:"status"`
Total int `json:"total_records"`
Domain Domain `json:"domain"`
Records []Record `json:"records"`
}
// GetRecordID - helper to get the id of a record
// Input: domainID, record, recordType
// Output: int
func (vega *VegaDNSClient) GetRecordID(domainID int, record string, recordType string) (int, error) {
params := make(map[string]string)
params["domain_id"] = fmt.Sprintf("%d", domainID)
resp, err := vega.Send("GET", "records", params)
if err != nil {
return -1, fmt.Errorf("Error sending GET to GetRecordID: %s", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return -1, fmt.Errorf("Error reading response from GetRecordID: %s", err)
}
if resp.StatusCode != http.StatusOK {
return -1, fmt.Errorf("Got bad answer from VegaDNS on GetRecordID. Code: %d. Message: %s", resp.StatusCode, string(body))
}
answer := RecordsResponse{}
if err := json.Unmarshal(body, &answer); err != nil {
return -1, fmt.Errorf("Error unmarshalling body from GetRecordID: %s", err)
}
for _, r := range answer.Records {
if r.Name == record && r.RecordType == recordType {
return r.RecordID, nil
}
}
return -1, errors.New("Couldnt find record")
}
// CreateTXT - Creates a TXT record
// Input: domainID, fqdn, value, ttl
// Output: nil or error
func (vega *VegaDNSClient) CreateTXT(domainID int, fqdn string, value string, ttl int) error {
params := make(map[string]string)
params["record_type"] = "TXT"
params["ttl"] = fmt.Sprintf("%d", ttl)
params["domain_id"] = fmt.Sprintf("%d", domainID)
params["name"] = strings.TrimSuffix(fqdn, ".")
params["value"] = value
resp, err := vega.Send("POST", "records", params)
if err != nil {
return fmt.Errorf("Send POST error in CreateTXT: %s", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("Error reading POST response in CreateTXT: %s", err)
}
if resp.StatusCode != http.StatusCreated {
return fmt.Errorf("Got bad answer from VegaDNS on CreateTXT. Code: %d. Message: %s", resp.StatusCode, string(body))
}
return nil
}
// DeleteRecord - deletes a record by id
// Input: recordID
// Output: nil or error
func (vega *VegaDNSClient) DeleteRecord(recordID int) error {
resp, err := vega.Send("DELETE", fmt.Sprintf("records/%d", recordID), nil)
if err != nil {
return fmt.Errorf("Send DELETE error in DeleteTXT: %s", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("Error reading DELETE response in DeleteTXT: %s", err)
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("Got bad answer from VegaDNS on DeleteTXT. Code: %d. Message: %s", resp.StatusCode, string(body))
}
return nil
}

74
vendor/github.com/OpenDNS/vegadns2client/tokens.go generated vendored Normal file
View file

@ -0,0 +1,74 @@
package vegadns2client
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"strings"
"time"
)
// Token - struct to hold token information
type Token struct {
Token string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
ExpiresAt time.Time
}
func (t Token) valid() error {
if time.Now().UTC().After(t.ExpiresAt) {
return errors.New("Token Expired")
}
return nil
}
func (vega *VegaDNSClient) getBearer() string {
if vega.token.valid() != nil {
vega.getAuthToken()
}
return vega.token.formatBearer()
}
func (t Token) formatBearer() string {
return fmt.Sprintf("Bearer %s", t.Token)
}
func (vega *VegaDNSClient) getAuthToken() {
tokenEndpoint := vega.getURL("token")
v := url.Values{}
v.Set("grant_type", "client_credentials")
req, err := http.NewRequest("POST", tokenEndpoint, strings.NewReader(v.Encode()))
if err != nil {
log.Fatalf("Error forming POST to getAuthToken: %s", err)
}
req.SetBasicAuth(vega.APIKey, vega.APISecret)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
issueTime := time.Now().UTC()
resp, err := vega.client.Do(req)
if err != nil {
log.Fatalf("Error sending POST to getAuthToken: %s", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Error reading response from POST to getAuthToken: %s", err)
}
if resp.StatusCode != http.StatusOK {
log.Fatalf("Got bad answer from VegaDNS on getAuthToken. Code: %d. Message: %s", resp.StatusCode, string(body))
}
if err := json.Unmarshal(body, &vega.token); err != nil {
log.Fatalf("Error unmarshalling body of POST to getAuthToken: %s", err)
}
if vega.token.TokenType != "bearer" {
log.Fatal("We don't support anything except bearer tokens")
}
vega.token.ExpiresAt = issueTime.Add(time.Duration(vega.token.ExpiresIn) * time.Second)
}