summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/xenolf/lego/providers/dns/rackspace
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/xenolf/lego/providers/dns/rackspace')
-rw-r--r--vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go284
-rw-r--r--vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace_test.go220
2 files changed, 0 insertions, 504 deletions
diff --git a/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go b/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go
deleted file mode 100644
index 13daa8c8a..000000000
--- a/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go
+++ /dev/null
@@ -1,284 +0,0 @@
-// Package rackspace implements a DNS provider for solving the DNS-01
-// challenge using rackspace DNS.
-package rackspace
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "os"
- "time"
-
- "github.com/xenolf/lego/acme"
-)
-
-// rackspaceAPIURL represents the Identity API endpoint to call
-var rackspaceAPIURL = "https://identity.api.rackspacecloud.com/v2.0/tokens"
-
-// DNSProvider is an implementation of the acme.ChallengeProvider interface
-// used to store the reusable token and DNS API endpoint
-type DNSProvider struct {
- token string
- cloudDNSEndpoint string
-}
-
-// NewDNSProvider returns a DNSProvider instance configured for Rackspace.
-// Credentials must be passed in the environment variables: RACKSPACE_USER
-// and RACKSPACE_API_KEY.
-func NewDNSProvider() (*DNSProvider, error) {
- user := os.Getenv("RACKSPACE_USER")
- key := os.Getenv("RACKSPACE_API_KEY")
- return NewDNSProviderCredentials(user, key)
-}
-
-// NewDNSProviderCredentials uses the supplied credentials to return a
-// DNSProvider instance configured for Rackspace. It authenticates against
-// the API, also grabbing the DNS Endpoint.
-func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) {
- if user == "" || key == "" {
- return nil, fmt.Errorf("Rackspace credentials missing")
- }
-
- type APIKeyCredentials struct {
- Username string `json:"username"`
- APIKey string `json:"apiKey"`
- }
-
- type Auth struct {
- APIKeyCredentials `json:"RAX-KSKEY:apiKeyCredentials"`
- }
-
- type RackspaceAuthData struct {
- Auth `json:"auth"`
- }
-
- type RackspaceIdentity struct {
- Access struct {
- ServiceCatalog []struct {
- Endpoints []struct {
- PublicURL string `json:"publicURL"`
- TenantID string `json:"tenantId"`
- } `json:"endpoints"`
- Name string `json:"name"`
- } `json:"serviceCatalog"`
- Token struct {
- ID string `json:"id"`
- } `json:"token"`
- } `json:"access"`
- }
-
- authData := RackspaceAuthData{
- Auth: Auth{
- APIKeyCredentials: APIKeyCredentials{
- Username: user,
- APIKey: key,
- },
- },
- }
-
- body, err := json.Marshal(authData)
- if err != nil {
- return nil, err
- }
-
- req, err := http.NewRequest("POST", rackspaceAPIURL, bytes.NewReader(body))
- if err != nil {
- return nil, err
- }
- req.Header.Set("Content-Type", "application/json")
-
- client := http.Client{Timeout: 30 * time.Second}
- resp, err := client.Do(req)
- if err != nil {
- return nil, fmt.Errorf("Error querying Rackspace Identity API: %v", err)
- }
- defer resp.Body.Close()
-
- if resp.StatusCode != http.StatusOK {
- return nil, fmt.Errorf("Rackspace Authentication failed. Response code: %d", resp.StatusCode)
- }
-
- var rackspaceIdentity RackspaceIdentity
- err = json.NewDecoder(resp.Body).Decode(&rackspaceIdentity)
- if err != nil {
- return nil, err
- }
-
- // Iterate through the Service Catalog to get the DNS Endpoint
- var dnsEndpoint string
- for _, service := range rackspaceIdentity.Access.ServiceCatalog {
- if service.Name == "cloudDNS" {
- dnsEndpoint = service.Endpoints[0].PublicURL
- break
- }
- }
- if dnsEndpoint == "" {
- return nil, fmt.Errorf("Failed to populate DNS endpoint, check Rackspace API for changes.")
- }
-
- return &DNSProvider{
- token: rackspaceIdentity.Access.Token.ID,
- cloudDNSEndpoint: dnsEndpoint,
- }, nil
-}
-
-// Present creates a TXT record to fulfil the dns-01 challenge
-func (c *DNSProvider) Present(domain, token, keyAuth string) error {
- fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
- zoneID, err := c.getHostedZoneID(fqdn)
- if err != nil {
- return err
- }
-
- rec := RackspaceRecords{
- RackspaceRecord: []RackspaceRecord{{
- Name: acme.UnFqdn(fqdn),
- Type: "TXT",
- Data: value,
- TTL: 300,
- }},
- }
-
- body, err := json.Marshal(rec)
- if err != nil {
- return err
- }
-
- _, err = c.makeRequest("POST", fmt.Sprintf("/domains/%d/records", zoneID), bytes.NewReader(body))
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// CleanUp removes the TXT record matching the specified parameters
-func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
- fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
- zoneID, err := c.getHostedZoneID(fqdn)
- if err != nil {
- return err
- }
-
- record, err := c.findTxtRecord(fqdn, zoneID)
- if err != nil {
- return err
- }
-
- _, err = c.makeRequest("DELETE", fmt.Sprintf("/domains/%d/records?id=%s", zoneID, record.ID), nil)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// getHostedZoneID performs a lookup to get the DNS zone which needs
-// modifying for a given FQDN
-func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) {
- // HostedZones represents the response when querying Rackspace DNS zones
- type ZoneSearchResponse struct {
- TotalEntries int `json:"totalEntries"`
- HostedZones []struct {
- ID int `json:"id"`
- Name string `json:"name"`
- } `json:"domains"`
- }
-
- authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
- if err != nil {
- return 0, err
- }
-
- result, err := c.makeRequest("GET", fmt.Sprintf("/domains?name=%s", acme.UnFqdn(authZone)), nil)
- if err != nil {
- return 0, err
- }
-
- var zoneSearchResponse ZoneSearchResponse
- err = json.Unmarshal(result, &zoneSearchResponse)
- if err != nil {
- return 0, err
- }
-
- // If nothing was returned, or for whatever reason more than 1 was returned (the search uses exact match, so should not occur)
- if zoneSearchResponse.TotalEntries != 1 {
- return 0, fmt.Errorf("Found %d zones for %s in Rackspace for domain %s", zoneSearchResponse.TotalEntries, authZone, fqdn)
- }
-
- return zoneSearchResponse.HostedZones[0].ID, nil
-}
-
-// findTxtRecord searches a DNS zone for a TXT record with a specific name
-func (c *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*RackspaceRecord, error) {
- result, err := c.makeRequest("GET", fmt.Sprintf("/domains/%d/records?type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), nil)
- if err != nil {
- return nil, err
- }
-
- var records RackspaceRecords
- err = json.Unmarshal(result, &records)
- if err != nil {
- return nil, err
- }
-
- recordsLength := len(records.RackspaceRecord)
- switch recordsLength {
- case 1:
- break
- case 0:
- return nil, fmt.Errorf("No TXT record found for %s", fqdn)
- default:
- return nil, fmt.Errorf("More than 1 TXT record found for %s", fqdn)
- }
-
- return &records.RackspaceRecord[0], nil
-}
-
-// makeRequest is a wrapper function used for making DNS API requests
-func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) {
- url := c.cloudDNSEndpoint + uri
- req, err := http.NewRequest(method, url, body)
- if err != nil {
- return nil, err
- }
-
- req.Header.Set("X-Auth-Token", c.token)
- req.Header.Set("Content-Type", "application/json")
-
- client := http.Client{Timeout: 30 * time.Second}
- resp, err := client.Do(req)
- if err != nil {
- return nil, fmt.Errorf("Error querying DNS API: %v", err)
- }
-
- defer resp.Body.Close()
-
- if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted {
- return nil, fmt.Errorf("Request failed for %s %s. Response code: %d", method, url, resp.StatusCode)
- }
-
- var r json.RawMessage
- err = json.NewDecoder(resp.Body).Decode(&r)
- if err != nil {
- return nil, fmt.Errorf("JSON decode failed for %s %s. Response code: %d", method, url, resp.StatusCode)
- }
-
- return r, nil
-}
-
-// RackspaceRecords is the list of records sent/received from the DNS API
-type RackspaceRecords struct {
- RackspaceRecord []RackspaceRecord `json:"records"`
-}
-
-// RackspaceRecord represents a Rackspace DNS record
-type RackspaceRecord struct {
- Name string `json:"name"`
- Type string `json:"type"`
- Data string `json:"data"`
- TTL int `json:"ttl,omitempty"`
- ID string `json:"id,omitempty"`
-}
diff --git a/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace_test.go b/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace_test.go
deleted file mode 100644
index 22c979cad..000000000
--- a/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace_test.go
+++ /dev/null
@@ -1,220 +0,0 @@
-package rackspace
-
-import (
- "fmt"
- "io/ioutil"
- "net/http"
- "net/http/httptest"
- "os"
- "strings"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
-)
-
-var (
- rackspaceLiveTest bool
- rackspaceUser string
- rackspaceAPIKey string
- rackspaceDomain string
- testAPIURL string
-)
-
-func init() {
- rackspaceUser = os.Getenv("RACKSPACE_USER")
- rackspaceAPIKey = os.Getenv("RACKSPACE_API_KEY")
- rackspaceDomain = os.Getenv("RACKSPACE_DOMAIN")
- if len(rackspaceUser) > 0 && len(rackspaceAPIKey) > 0 && len(rackspaceDomain) > 0 {
- rackspaceLiveTest = true
- }
-}
-
-func testRackspaceEnv() {
- rackspaceAPIURL = testAPIURL
- os.Setenv("RACKSPACE_USER", "testUser")
- os.Setenv("RACKSPACE_API_KEY", "testKey")
-}
-
-func liveRackspaceEnv() {
- rackspaceAPIURL = "https://identity.api.rackspacecloud.com/v2.0/tokens"
- os.Setenv("RACKSPACE_USER", rackspaceUser)
- os.Setenv("RACKSPACE_API_KEY", rackspaceAPIKey)
-}
-
-func startTestServers() (identityAPI, dnsAPI *httptest.Server) {
- dnsAPI = httptest.NewServer(dnsMux())
- dnsEndpoint := dnsAPI.URL + "/123456"
-
- identityAPI = httptest.NewServer(identityHandler(dnsEndpoint))
- testAPIURL = identityAPI.URL + "/"
- return
-}
-
-func closeTestServers(identityAPI, dnsAPI *httptest.Server) {
- identityAPI.Close()
- dnsAPI.Close()
-}
-
-func identityHandler(dnsEndpoint string) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- reqBody, err := ioutil.ReadAll(r.Body)
- if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
- return
- }
- resp, found := jsonMap[string(reqBody)]
- if !found {
- w.WriteHeader(http.StatusBadRequest)
- return
- }
- resp = strings.Replace(resp, "https://dns.api.rackspacecloud.com/v1.0/123456", dnsEndpoint, 1)
- w.WriteHeader(http.StatusOK)
- fmt.Fprintf(w, resp)
- })
-}
-
-func dnsMux() *http.ServeMux {
- mux := http.NewServeMux()
-
- // Used by `getHostedZoneID()` finding `zoneID` "?name=example.com"
- mux.HandleFunc("/123456/domains", func(w http.ResponseWriter, r *http.Request) {
- if r.URL.Query().Get("name") == "example.com" {
- w.WriteHeader(http.StatusOK)
- fmt.Fprintf(w, jsonMap["zoneDetails"])
- return
- }
- w.WriteHeader(http.StatusBadRequest)
- return
- })
-
- mux.HandleFunc("/123456/domains/112233/records", func(w http.ResponseWriter, r *http.Request) {
- switch r.Method {
- // Used by `Present()` creating the TXT record
- case http.MethodPost:
- reqBody, err := ioutil.ReadAll(r.Body)
- if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
- return
- }
- resp, found := jsonMap[string(reqBody)]
- if !found {
- w.WriteHeader(http.StatusBadRequest)
- return
- }
- w.WriteHeader(http.StatusAccepted)
- fmt.Fprintf(w, resp)
- // Used by `findTxtRecord()` finding `record.ID` "?type=TXT&name=_acme-challenge.example.com"
- case http.MethodGet:
- if r.URL.Query().Get("type") == "TXT" && r.URL.Query().Get("name") == "_acme-challenge.example.com" {
- w.WriteHeader(http.StatusOK)
- fmt.Fprintf(w, jsonMap["recordDetails"])
- return
- }
- w.WriteHeader(http.StatusBadRequest)
- return
- // Used by `CleanUp()` deleting the TXT record "?id=445566"
- case http.MethodDelete:
- if r.URL.Query().Get("id") == "TXT-654321" {
- w.WriteHeader(http.StatusOK)
- fmt.Fprintf(w, jsonMap["recordDelete"])
- 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 TestNewDNSProviderMissingCredErr(t *testing.T) {
- testRackspaceEnv()
- _, err := NewDNSProviderCredentials("", "")
- assert.EqualError(t, err, "Rackspace credentials missing")
-}
-
-func TestOfflineRackspaceValid(t *testing.T) {
- testRackspaceEnv()
- provider, err := NewDNSProviderCredentials(os.Getenv("RACKSPACE_USER"), os.Getenv("RACKSPACE_API_KEY"))
-
- assert.NoError(t, err)
- assert.Equal(t, provider.token, "testToken", "The token should match")
-}
-
-func TestOfflineRackspacePresent(t *testing.T) {
- testRackspaceEnv()
- provider, err := NewDNSProvider()
-
- if assert.NoError(t, err) {
- err = provider.Present("example.com", "token", "keyAuth")
- assert.NoError(t, err)
- }
-}
-
-func TestOfflineRackspaceCleanUp(t *testing.T) {
- testRackspaceEnv()
- provider, err := NewDNSProvider()
-
- if assert.NoError(t, err) {
- err = provider.CleanUp("example.com", "token", "keyAuth")
- assert.NoError(t, err)
- }
-}
-
-func TestNewDNSProviderValidEnv(t *testing.T) {
- if !rackspaceLiveTest {
- t.Skip("skipping live test")
- }
-
- liveRackspaceEnv()
- provider, err := NewDNSProvider()
- assert.NoError(t, err)
- assert.Contains(t, provider.cloudDNSEndpoint, "https://dns.api.rackspacecloud.com/v1.0/", "The endpoint URL should contain the base")
-}
-
-func TestRackspacePresent(t *testing.T) {
- if !rackspaceLiveTest {
- t.Skip("skipping live test")
- }
-
- liveRackspaceEnv()
- provider, err := NewDNSProvider()
- assert.NoError(t, err)
-
- err = provider.Present(rackspaceDomain, "", "112233445566==")
- assert.NoError(t, err)
-}
-
-func TestRackspaceCleanUp(t *testing.T) {
- if !rackspaceLiveTest {
- t.Skip("skipping live test")
- }
-
- time.Sleep(time.Second * 15)
-
- liveRackspaceEnv()
- provider, err := NewDNSProvider()
- assert.NoError(t, err)
-
- err = provider.CleanUp(rackspaceDomain, "", "112233445566==")
- assert.NoError(t, err)
-}
-
-func TestMain(m *testing.M) {
- identityAPI, dnsAPI := startTestServers()
- defer closeTestServers(identityAPI, dnsAPI)
- os.Exit(m.Run())
-}
-
-var jsonMap = map[string]string{
- `{"auth":{"RAX-KSKEY:apiKeyCredentials":{"username":"testUser","apiKey":"testKey"}}}`: `{"access":{"token":{"id":"testToken","expires":"1970-01-01T00:00:00.000Z","tenant":{"id":"123456","name":"123456"},"RAX-AUTH:authenticatedBy":["APIKEY"]},"serviceCatalog":[{"type":"rax:dns","endpoints":[{"publicURL":"https://dns.api.rackspacecloud.com/v1.0/123456","tenantId":"123456"}],"name":"cloudDNS"}],"user":{"id":"fakeUseID","name":"testUser"}}}`,
- "zoneDetails": `{"domains":[{"name":"example.com","id":112233,"emailAddress":"hostmaster@example.com","updated":"1970-01-01T00:00:00.000+0000","created":"1970-01-01T00:00:00.000+0000"}],"totalEntries":1}`,
- `{"records":[{"name":"_acme-challenge.example.com","type":"TXT","data":"pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM","ttl":300}]}`: `{"request":"{\"records\":[{\"name\":\"_acme-challenge.example.com\",\"type\":\"TXT\",\"data\":\"pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM\",\"ttl\":300}]}","status":"RUNNING","verb":"POST","jobId":"00000000-0000-0000-0000-0000000000","callbackUrl":"https://dns.api.rackspacecloud.com/v1.0/123456/status/00000000-0000-0000-0000-0000000000","requestUrl":"https://dns.api.rackspacecloud.com/v1.0/123456/domains/112233/records"}`,
- "recordDetails": `{"records":[{"name":"_acme-challenge.example.com","id":"TXT-654321","type":"TXT","data":"pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM","ttl":300,"updated":"1970-01-01T00:00:00.000+0000","created":"1970-01-01T00:00:00.000+0000"}]}`,
- "recordDelete": `{"status":"RUNNING","verb":"DELETE","jobId":"00000000-0000-0000-0000-0000000000","callbackUrl":"https://dns.api.rackspacecloud.com/v1.0/123456/status/00000000-0000-0000-0000-0000000000","requestUrl":"https://dns.api.rackspacecloud.com/v1.0/123456/domains/112233/recordsid=TXT-654321"}`,
-}