Skip to content

Commit

Permalink
add ConoHa DNS provider (go-acme#692)
Browse files Browse the repository at this point in the history
  • Loading branch information
kaz authored and ldez committed Nov 2, 2018
1 parent 2b0aa0a commit 8556397
Show file tree
Hide file tree
Showing 7 changed files with 735 additions and 0 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ owners to license your work under the terms of the [MIT License](LICENSE).
| Bluecat | `bluecat` | ? | - |
| Cloudflare | `cloudflare` | [documentation](https://api.cloudflare.com/) | [Go client](https://github.com/cloudflare/cloudflare-go) |
| CloudXNS | `cloudxns` | [documentation](https://www.cloudxns.net/Public/Doc/CloudXNS_api2.0_doc_zh-cn.zip) | - |
| ConoHa | `conoha` | [documentation](https://www.conoha.jp/docs/) | - |
| Digital Ocean | `digitalocean` | [documentation](https://developers.digitalocean.com/documentation/v2/#domain-records) | - |
| DNSimple | `dnsimple` | [documentation](https://developer.dnsimple.com/v2/) | [Go client](https://github.com/dnsimple/dnsimple-go) |
| DNS Made Easy | `dnsmadeeasy` | [documentation](https://api-docs.dnsmadeeasy.com/) | - |
Expand Down
2 changes: 2 additions & 0 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ Here is an example bash command using the CloudFlare DNS provider:
fmt.Fprintln(w, "\tbluecat:\tBLUECAT_SERVER_URL, BLUECAT_USER_NAME, BLUECAT_PASSWORD, BLUECAT_CONFIG_NAME, BLUECAT_DNS_VIEW")
fmt.Fprintln(w, "\tcloudflare:\tCLOUDFLARE_EMAIL, CLOUDFLARE_API_KEY")
fmt.Fprintln(w, "\tcloudxns:\tCLOUDXNS_API_KEY, CLOUDXNS_SECRET_KEY")
fmt.Fprintln(w, "\tconoha:\tCONOHA_REGION, CONOHA_TENANT_ID, CONOHA_API_USERNAME, CONOHA_API_PASSWORD")
fmt.Fprintln(w, "\tdigitalocean:\tDO_AUTH_TOKEN")
fmt.Fprintln(w, "\tdnsimple:\tDNSIMPLE_EMAIL, DNSIMPLE_OAUTH_TOKEN")
fmt.Fprintln(w, "\tdnsmadeeasy:\tDNSMADEEASY_API_KEY, DNSMADEEASY_API_SECRET")
Expand Down Expand Up @@ -254,6 +255,7 @@ Here is an example bash command using the CloudFlare DNS provider:
fmt.Fprintln(w, "\tbluecat:\tBLUECAT_POLLING_INTERVAL, BLUECAT_PROPAGATION_TIMEOUT, BLUECAT_TTL, BLUECAT_HTTP_TIMEOUT")
fmt.Fprintln(w, "\tcloudflare:\tCLOUDFLARE_POLLING_INTERVAL, CLOUDFLARE_PROPAGATION_TIMEOUT, CLOUDFLARE_TTL, CLOUDFLARE_HTTP_TIMEOUT")
fmt.Fprintln(w, "\tcloudxns:\tCLOUDXNS_POLLING_INTERVAL, CLOUDXNS_PROPAGATION_TIMEOUT, CLOUDXNS_TTL, CLOUDXNS_HTTP_TIMEOUT")
fmt.Fprintln(w, "\tconoha:\tCONOHA_POLLING_INTERVAL, CONOHA_PROPAGATION_TIMEOUT, CONOHA_TTL, CONOHA_HTTP_TIMEOUT")
fmt.Fprintln(w, "\tdigitalocean:\tDO_POLLING_INTERVAL, DO_PROPAGATION_TIMEOUT, DO_TTL, DO_HTTP_TIMEOUT")
fmt.Fprintln(w, "\tdnsimple:\tDNSIMPLE_TTL, DNSIMPLE_POLLING_INTERVAL, DNSIMPLE_PROPAGATION_TIMEOUT")
fmt.Fprintln(w, "\tdnsmadeeasy:\tDNSMADEEASY_POLLING_INTERVAL, DNSMADEEASY_PROPAGATION_TIMEOUT, DNSMADEEASY_TTL, DNSMADEEASY_HTTP_TIMEOUT")
Expand Down
205 changes: 205 additions & 0 deletions providers/dns/conoha/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package conoha

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
)

const (
identityBaseURL = "https://identity.%s.conoha.io"
dnsServiceBaseURL = "https://dns-service.%s.conoha.io"
)

// IdentityRequest is an authentication request body.
type IdentityRequest struct {
Auth Auth `json:"auth"`
}

// Auth is an authentication information.
type Auth struct {
TenantID string `json:"tenantId"`
PasswordCredentials PasswordCredentials `json:"passwordCredentials"`
}

// PasswordCredentials is API-user's credentials.
type PasswordCredentials struct {
Username string `json:"username"`
Password string `json:"password"`
}

// IdentityResponse is an authentication response body.
type IdentityResponse struct {
Access Access `json:"access"`
}

// Access is an identity information.
type Access struct {
Token Token `json:"token"`
}

// Token is an api access token.
type Token struct {
ID string `json:"id"`
}

// DomainListResponse is a response of a domain listing request.
type DomainListResponse struct {
Domains []Domain `json:"domains"`
}

// Domain is a hosted domain entry.
type Domain struct {
ID string `json:"id"`
Name string `json:"name"`
}

// RecordListResponse is a response of record listing request.
type RecordListResponse struct {
Records []Record `json:"records"`
}

// Record is a record entry.
type Record struct {
ID string `json:"id,omitempty"`
Name string `json:"name"`
Type string `json:"type"`
Data string `json:"data"`
TTL int `json:"ttl"`
}

// Client is a ConoHa API client.
type Client struct {
token string
endpoint string
httpClient *http.Client
}

// NewClient returns a client instance logged into the ConoHa service.
func NewClient(region string, auth Auth, httpClient *http.Client) (*Client, error) {
if httpClient == nil {
httpClient = &http.Client{}
}

c := &Client{httpClient: httpClient}

c.endpoint = fmt.Sprintf(identityBaseURL, region)

identity, err := c.getIdentity(auth)
if err != nil {
return nil, fmt.Errorf("failed to login: %v", err)
}

c.token = identity.Access.Token.ID
c.endpoint = fmt.Sprintf(dnsServiceBaseURL, region)

return c, nil
}

func (c *Client) getIdentity(auth Auth) (*IdentityResponse, error) {
req := &IdentityRequest{Auth: auth}

identity := &IdentityResponse{}

err := c.do(http.MethodPost, "/v2.0/tokens", req, identity)
if err != nil {
return nil, err
}

return identity, nil
}

// GetDomainID returns an ID of specified domain.
func (c *Client) GetDomainID(domainName string) (string, error) {
domainList := &DomainListResponse{}

err := c.do(http.MethodGet, "/v1/domains", nil, domainList)
if err != nil {
return "", err
}

for _, domain := range domainList.Domains {
if domain.Name == domainName {
return domain.ID, nil
}
}
return "", fmt.Errorf("no such domain: %s", domainName)
}

// GetRecordID returns an ID of specified record.
func (c *Client) GetRecordID(domainID, recordName, recordType, data string) (string, error) {
recordList := &RecordListResponse{}

err := c.do(http.MethodGet, fmt.Sprintf("/v1/domains/%s/records", domainID), nil, recordList)
if err != nil {
return "", err
}

for _, record := range recordList.Records {
if record.Name == recordName && record.Type == recordType && record.Data == data {
return record.ID, nil
}
}
return "", errors.New("no such record")
}

// CreateRecord adds new record.
func (c *Client) CreateRecord(domainID string, record Record) error {
return c.do(http.MethodPost, fmt.Sprintf("/v1/domains/%s/records", domainID), record, nil)
}

// DeleteRecord removes specified record.
func (c *Client) DeleteRecord(domainID, recordID string) error {
return c.do(http.MethodDelete, fmt.Sprintf("/v1/domains/%s/records/%s", domainID, recordID), nil, nil)
}

func (c *Client) do(method, path string, payload, result interface{}) error {
body := bytes.NewReader(nil)

if payload != nil {
bodyBytes, err := json.Marshal(payload)
if err != nil {
return err
}
body = bytes.NewReader(bodyBytes)
}

req, err := http.NewRequest(method, c.endpoint+path, body)
if err != nil {
return err
}

req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Auth-Token", c.token)

resp, err := c.httpClient.Do(req)
if err != nil {
return err
}

if resp.StatusCode != http.StatusOK {
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
defer resp.Body.Close()

return fmt.Errorf("HTTP request failed with status code %d: %s", resp.StatusCode, string(respBody))
}

if result != nil {
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
defer resp.Body.Close()

return json.Unmarshal(respBody, result)
}

return nil
}
Loading

0 comments on commit 8556397

Please sign in to comment.