Update lego

This commit is contained in:
Ed Robinson 2017-04-07 10:53:39 +01:00
parent 65284441fa
commit a3b95f798b
No known key found for this signature in database
GPG key ID: EC501FCA6421CCF0
61 changed files with 3453 additions and 1536 deletions

19
glide.lock generated
View file

@ -1,5 +1,5 @@
hash: 8349c21c53c639aa79752c4e4b07e323ddb4f05be783564d7de687afbb6f2d67
updated: 2017-04-07T10:06:47.901457262+01:00
hash: fa4c40400ed94e105a9a1a0d163ac9c4ecea4598ad4ee4fef5237a39d4f28d0a
updated: 2017-04-07T10:50:34.091189379+01:00
imports:
- name: bitbucket.org/ww/goautoneg
version: 75cd24fc2f2c2a2088577d12123ddee5f54e0675
@ -53,17 +53,16 @@ imports:
- service/route53
- service/sts
- name: github.com/Azure/azure-sdk-for-go
version: 1620af6b32398bfc91827ceae54a8cc1f55df04d
version: 088007b3b08cc02b27f2eadfdcd870958460ce7e
subpackages:
- arm/dns
- name: github.com/Azure/go-autorest
version: 32cc2321122a649b7ba4e323527bcb145134fd47
version: a2fdd780c9a50455cecd249b00bdc3eb73a78e31
subpackages:
- autorest
- autorest/azure
- autorest/date
- autorest/to
- autorest/validation
- name: github.com/beorn7/perks
version: b965b613227fddccbfffe13eae360ed3fa822f8d
subpackages:
@ -129,6 +128,10 @@ imports:
version: f6b1d56f1c048bd94d7e42ac36efb4d57b069b6f
- name: github.com/dgrijalva/jwt-go
version: 9ed569b5d1ac936e6494082958d63a6aa4fff99a
- name: github.com/dnsimple/dnsimple-go
version: 5a5b427618a76f9eed5ede0f3e6306fbd9311d2e
subpackages:
- dnsimple
- name: github.com/docker/distribution
version: 325b0804fef3a66309d962357aac3c2ce3f4d329
subpackages:
@ -415,12 +418,8 @@ imports:
- plugin
- plugin/rewrite
- router
- name: github.com/weppos/dnsimple-go
version: 65c1ca73cb19baf0f8b2b33219b7f57595a3ccb0
subpackages:
- dnsimple
- name: github.com/xenolf/lego
version: ce8fb060cb8361a9ff8b5fb7c2347fa907b6fcac
version: 5dfe609afb1ebe9da97c9846d97a55415e5a5ccd
subpackages:
- acme
- providers/dns

View file

@ -60,7 +60,7 @@ import:
subpackages:
- plugin/rewrite
- package: github.com/xenolf/lego
version: ce8fb060cb8361a9ff8b5fb7c2347fa907b6fcac
version: 5dfe609afb1ebe9da97c9846d97a55415e5a5ccd
subpackages:
- acme
- package: gopkg.in/fsnotify.v1

7
vendor/github.com/Azure/azure-sdk-for-go/arm/dns/client.go generated vendored Normal file → Executable file
View file

@ -17,7 +17,7 @@ package dns
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 0.17.0.0
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
@ -26,9 +26,6 @@ import (
)
const (
// APIVersion is the version of the Dns
APIVersion = "2016-04-01"
// DefaultBaseURI is the default URI used for the service Dns
DefaultBaseURI = "https://management.azure.com"
)
@ -37,7 +34,6 @@ const (
type ManagementClient struct {
autorest.Client
BaseURI string
APIVersion string
SubscriptionID string
}
@ -51,7 +47,6 @@ func NewWithBaseURI(baseURI string, subscriptionID string) ManagementClient {
return ManagementClient{
Client: autorest.NewClientWithUserAgent(UserAgent()),
BaseURI: baseURI,
APIVersion: APIVersion,
SubscriptionID: subscriptionID,
}
}

30
vendor/github.com/Azure/azure-sdk-for-go/arm/dns/models.go generated vendored Normal file → Executable file
View file

@ -14,7 +14,7 @@ package dns
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 0.17.0.0
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
@ -42,8 +42,8 @@ const (
Continue HTTPStatusCode = "Continue"
// Created specifies the created state for http status code.
Created HTTPStatusCode = "Created"
// ExpectationFailed specifies the expectation failed state for http
// status code.
// ExpectationFailed specifies the expectation failed state for http status
// code.
ExpectationFailed HTTPStatusCode = "ExpectationFailed"
// Forbidden specifies the forbidden state for http status code.
Forbidden HTTPStatusCode = "Forbidden"
@ -126,13 +126,13 @@ const (
// SwitchingProtocols specifies the switching protocols state for http
// status code.
SwitchingProtocols HTTPStatusCode = "SwitchingProtocols"
// TemporaryRedirect specifies the temporary redirect state for http
// status code.
// TemporaryRedirect specifies the temporary redirect state for http status
// code.
TemporaryRedirect HTTPStatusCode = "TemporaryRedirect"
// Unauthorized specifies the unauthorized state for http status code.
Unauthorized HTTPStatusCode = "Unauthorized"
// UnsupportedMediaType specifies the unsupported media type state for
// http status code.
// UnsupportedMediaType specifies the unsupported media type state for http
// status code.
UnsupportedMediaType HTTPStatusCode = "UnsupportedMediaType"
// Unused specifies the unused state for http status code.
Unused HTTPStatusCode = "Unused"
@ -259,14 +259,14 @@ type RecordSetProperties struct {
Metadata *map[string]*string `json:"metadata,omitempty"`
TTL *int64 `json:"TTL,omitempty"`
ARecords *[]ARecord `json:"ARecords,omitempty"`
AAAARecords *[]AaaaRecord `json:"AAAARecords,omitempty"`
MXRecords *[]MxRecord `json:"MXRecords,omitempty"`
NSRecords *[]NsRecord `json:"NSRecords,omitempty"`
PTRRecords *[]PtrRecord `json:"PTRRecords,omitempty"`
SRVRecords *[]SrvRecord `json:"SRVRecords,omitempty"`
TXTRecords *[]TxtRecord `json:"TXTRecords,omitempty"`
CNAMERecord *CnameRecord `json:"CNAMERecord,omitempty"`
SOARecord *SoaRecord `json:"SOARecord,omitempty"`
AaaaRecords *[]AaaaRecord `json:"AAAARecords,omitempty"`
MxRecords *[]MxRecord `json:"MXRecords,omitempty"`
NsRecords *[]NsRecord `json:"NSRecords,omitempty"`
PtrRecords *[]PtrRecord `json:"PTRRecords,omitempty"`
SrvRecords *[]SrvRecord `json:"SRVRecords,omitempty"`
TxtRecords *[]TxtRecord `json:"TXTRecords,omitempty"`
CnameRecord *CnameRecord `json:"CNAMERecord,omitempty"`
SoaRecord *SoaRecord `json:"SOARecord,omitempty"`
}
// RecordSetUpdateParameters is parameters supplied to update a record set.

98
vendor/github.com/Azure/azure-sdk-for-go/arm/dns/recordsets.go generated vendored Normal file → Executable file
View file

@ -14,7 +14,7 @@ package dns
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 0.17.0.0
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
@ -42,18 +42,17 @@ func NewRecordSetsClientWithBaseURI(baseURI string, subscriptionID string) Recor
// CreateOrUpdate creates or updates a record set within a DNS zone.
//
// resourceGroupName is the name of the resource group. zoneName is the name
// of the DNS zone (without a terminating dot). relativeRecordSetName is the
// name of the record set, relative to the name of the zone. recordType is
// the type of DNS record in this record set. Record sets of type SOA can be
// updated but not created (they are created when the DNS zone is created).
// Possible values include: 'A', 'AAAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA',
// 'SRV', 'TXT' parameters is parameters supplied to the CreateOrUpdate
// operation. ifMatch is the etag of the record set. Omit this value to
// always overwrite the current record set. Specify the last-seen etag value
// to prevent accidentally overwritting any concurrent changes. ifNoneMatch
// is set to '*' to allow a new record set to be created, but to prevent
// updating an existing record set. Other values will be ignored.
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). relativeRecordSetName is the name
// of the record set, relative to the name of the zone. recordType is the type
// of DNS record in this record set. Record sets of type SOA can be updated but
// not created (they are created when the DNS zone is created). parameters is
// parameters supplied to the CreateOrUpdate operation. ifMatch is the etag of
// the record set. Omit this value to always overwrite the current record set.
// Specify the last-seen etag value to prevent accidentally overwritting any
// concurrent changes. ifNoneMatch is set to '*' to allow a new record set to
// be created, but to prevent updating an existing record set. Other values
// will be ignored.
func (client RecordSetsClient) CreateOrUpdate(resourceGroupName string, zoneName string, relativeRecordSetName string, recordType RecordType, parameters RecordSet, ifMatch string, ifNoneMatch string) (result RecordSet, err error) {
req, err := client.CreateOrUpdatePreparer(resourceGroupName, zoneName, relativeRecordSetName, recordType, parameters, ifMatch, ifNoneMatch)
if err != nil {
@ -84,8 +83,9 @@ func (client RecordSetsClient) CreateOrUpdatePreparer(resourceGroupName string,
"zoneName": autorest.Encode("path", zoneName),
}
const APIVersion = "2016-04-01"
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
"api-version": APIVersion,
}
preparer := autorest.CreatePreparer(
@ -128,15 +128,14 @@ func (client RecordSetsClient) CreateOrUpdateResponder(resp *http.Response) (res
// Delete deletes a record set from a DNS zone. This operation cannot be
// undone.
//
// resourceGroupName is the name of the resource group. zoneName is the name
// of the DNS zone (without a terminating dot). relativeRecordSetName is the
// name of the record set, relative to the name of the zone. recordType is
// the type of DNS record in this record set. Record sets of type SOA cannot
// be deleted (they are deleted when the DNS zone is deleted). Possible
// values include: 'A', 'AAAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV',
// 'TXT' ifMatch is the etag of the record set. Omit this value to always
// delete the current record set. Specify the last-seen etag value to prevent
// accidentally deleting any concurrent changes.
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). relativeRecordSetName is the name
// of the record set, relative to the name of the zone. recordType is the type
// of DNS record in this record set. Record sets of type SOA cannot be deleted
// (they are deleted when the DNS zone is deleted). ifMatch is the etag of the
// record set. Omit this value to always delete the current record set. Specify
// the last-seen etag value to prevent accidentally deleting any concurrent
// changes.
func (client RecordSetsClient) Delete(resourceGroupName string, zoneName string, relativeRecordSetName string, recordType RecordType, ifMatch string) (result autorest.Response, err error) {
req, err := client.DeletePreparer(resourceGroupName, zoneName, relativeRecordSetName, recordType, ifMatch)
if err != nil {
@ -167,8 +166,9 @@ func (client RecordSetsClient) DeletePreparer(resourceGroupName string, zoneName
"zoneName": autorest.Encode("path", zoneName),
}
const APIVersion = "2016-04-01"
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
"api-version": APIVersion,
}
preparer := autorest.CreatePreparer(
@ -203,11 +203,10 @@ func (client RecordSetsClient) DeleteResponder(resp *http.Response) (result auto
// Get gets a record set.
//
// resourceGroupName is the name of the resource group. zoneName is the name
// of the DNS zone (without a terminating dot). relativeRecordSetName is the
// name of the record set, relative to the name of the zone. recordType is
// the type of DNS record in this record set. Possible values include: 'A',
// 'AAAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT'
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). relativeRecordSetName is the name
// of the record set, relative to the name of the zone. recordType is the type
// of DNS record in this record set.
func (client RecordSetsClient) Get(resourceGroupName string, zoneName string, relativeRecordSetName string, recordType RecordType) (result RecordSet, err error) {
req, err := client.GetPreparer(resourceGroupName, zoneName, relativeRecordSetName, recordType)
if err != nil {
@ -238,8 +237,9 @@ func (client RecordSetsClient) GetPreparer(resourceGroupName string, zoneName st
"zoneName": autorest.Encode("path", zoneName),
}
const APIVersion = "2016-04-01"
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
"api-version": APIVersion,
}
preparer := autorest.CreatePreparer(
@ -271,8 +271,8 @@ func (client RecordSetsClient) GetResponder(resp *http.Response) (result RecordS
// ListByDNSZone lists all record sets in a DNS zone.
//
// resourceGroupName is the name of the resource group. zoneName is the name
// of the DNS zone (without a terminating dot). top is the maximum number of
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). top is the maximum number of
// record sets to return. If not specified, returns up to 100 record sets.
func (client RecordSetsClient) ListByDNSZone(resourceGroupName string, zoneName string, top *int32) (result RecordSetListResult, err error) {
req, err := client.ListByDNSZonePreparer(resourceGroupName, zoneName, top)
@ -302,8 +302,9 @@ func (client RecordSetsClient) ListByDNSZonePreparer(resourceGroupName string, z
"zoneName": autorest.Encode("path", zoneName),
}
const APIVersion = "2016-04-01"
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
"api-version": APIVersion,
}
if top != nil {
queryParameters["$top"] = autorest.Encode("query", *top)
@ -362,11 +363,10 @@ func (client RecordSetsClient) ListByDNSZoneNextResults(lastResults RecordSetLis
// ListByType lists the record sets of a specified type in a DNS zone.
//
// resourceGroupName is the name of the resource group. zoneName is the name
// of the DNS zone (without a terminating dot). recordType is the type of
// record sets to enumerate. Possible values include: 'A', 'AAAA', 'CNAME',
// 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' top is the maximum number of record
// sets to return. If not specified, returns up to 100 record sets.
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). recordType is the type of record
// sets to enumerate. top is the maximum number of record sets to return. If
// not specified, returns up to 100 record sets.
func (client RecordSetsClient) ListByType(resourceGroupName string, zoneName string, recordType RecordType, top *int32) (result RecordSetListResult, err error) {
req, err := client.ListByTypePreparer(resourceGroupName, zoneName, recordType, top)
if err != nil {
@ -396,8 +396,9 @@ func (client RecordSetsClient) ListByTypePreparer(resourceGroupName string, zone
"zoneName": autorest.Encode("path", zoneName),
}
const APIVersion = "2016-04-01"
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
"api-version": APIVersion,
}
if top != nil {
queryParameters["$top"] = autorest.Encode("query", *top)
@ -456,15 +457,13 @@ func (client RecordSetsClient) ListByTypeNextResults(lastResults RecordSetListRe
// Update updates a record set within a DNS zone.
//
// resourceGroupName is the name of the resource group. zoneName is the name
// of the DNS zone (without a terminating dot). relativeRecordSetName is the
// name of the record set, relative to the name of the zone. recordType is
// the type of DNS record in this record set. Possible values include: 'A',
// 'AAAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' parameters is
// parameters supplied to the Update operation. ifMatch is the etag of the
// record set. Omit this value to always overwrite the current record set.
// Specify the last-seen etag value to prevent accidentally overwritting
// concurrent changes.
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). relativeRecordSetName is the name
// of the record set, relative to the name of the zone. recordType is the type
// of DNS record in this record set. parameters is parameters supplied to the
// Update operation. ifMatch is the etag of the record set. Omit this value to
// always overwrite the current record set. Specify the last-seen etag value to
// prevent accidentally overwritting concurrent changes.
func (client RecordSetsClient) Update(resourceGroupName string, zoneName string, relativeRecordSetName string, recordType RecordType, parameters RecordSet, ifMatch string) (result RecordSet, err error) {
req, err := client.UpdatePreparer(resourceGroupName, zoneName, relativeRecordSetName, recordType, parameters, ifMatch)
if err != nil {
@ -495,8 +494,9 @@ func (client RecordSetsClient) UpdatePreparer(resourceGroupName string, zoneName
"zoneName": autorest.Encode("path", zoneName),
}
const APIVersion = "2016-04-01"
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
"api-version": APIVersion,
}
preparer := autorest.CreatePreparer(

20
vendor/github.com/Azure/azure-sdk-for-go/arm/dns/version.go generated vendored Normal file → Executable file
View file

@ -14,30 +14,16 @@ package dns
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 0.17.0.0
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
import (
"fmt"
)
const (
major = "7"
minor = "0"
patch = "1"
// Always begin a "tag" with a dash (as per http://semver.org)
tag = "-beta"
semVerFormat = "%s.%s.%s%s"
userAgentFormat = "Azure-SDK-for-Go/%s arm-%s/%s"
)
// UserAgent returns the UserAgent string to use when sending http.Requests.
func UserAgent() string {
return fmt.Sprintf(userAgentFormat, Version(), "dns", "2016-04-01")
return "Azure-SDK-For-Go/v9.0.0-beta arm-dns/2016-04-01"
}
// Version returns the semantic version (see http://semver.org) of the client.
func Version() string {
return fmt.Sprintf(semVerFormat, major, minor, patch, tag)
return "v9.0.0-beta"
}

63
vendor/github.com/Azure/azure-sdk-for-go/arm/dns/zones.go generated vendored Normal file → Executable file
View file

@ -14,14 +14,13 @@ package dns
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by Microsoft (R) AutoRest Code Generator 0.17.0.0
// Code generated by Microsoft (R) AutoRest Code Generator 1.0.1.0
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
import (
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/validation"
"net/http"
)
@ -43,21 +42,14 @@ func NewZonesClientWithBaseURI(baseURI string, subscriptionID string) ZonesClien
// CreateOrUpdate creates or updates a DNS zone. Does not modify DNS records
// within the zone.
//
// resourceGroupName is the name of the resource group. zoneName is the name
// of the DNS zone (without a terminating dot). parameters is parameters
// supplied to the CreateOrUpdate operation. ifMatch is the etag of the DNS
// zone. Omit this value to always overwrite the current zone. Specify the
// last-seen etag value to prevent accidentally overwritting any concurrent
// changes. ifNoneMatch is set to '*' to allow a new DNS zone to be created,
// but to prevent updating an existing zone. Other values will be ignored.
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). parameters is parameters supplied
// to the CreateOrUpdate operation. ifMatch is the etag of the DNS zone. Omit
// this value to always overwrite the current zone. Specify the last-seen etag
// value to prevent accidentally overwritting any concurrent changes.
// ifNoneMatch is set to '*' to allow a new DNS zone to be created, but to
// prevent updating an existing zone. Other values will be ignored.
func (client ZonesClient) CreateOrUpdate(resourceGroupName string, zoneName string, parameters Zone, ifMatch string, ifNoneMatch string) (result Zone, err error) {
if err := validation.Validate([]validation.Validation{
{TargetValue: parameters,
Constraints: []validation.Constraint{{Target: "parameters.ZoneProperties", Name: validation.Null, Rule: false,
Chain: []validation.Constraint{{Target: "parameters.ZoneProperties.NameServers", Name: validation.ReadOnly, Rule: true, Chain: nil}}}}}}); err != nil {
return result, validation.NewErrorWithValidationError(err, "dns.ZonesClient", "CreateOrUpdate")
}
req, err := client.CreateOrUpdatePreparer(resourceGroupName, zoneName, parameters, ifMatch, ifNoneMatch)
if err != nil {
return result, autorest.NewErrorWithError(err, "dns.ZonesClient", "CreateOrUpdate", nil, "Failure preparing request")
@ -85,8 +77,9 @@ func (client ZonesClient) CreateOrUpdatePreparer(resourceGroupName string, zoneN
"zoneName": autorest.Encode("path", zoneName),
}
const APIVersion = "2016-04-01"
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
"api-version": APIVersion,
}
preparer := autorest.CreatePreparer(
@ -126,15 +119,15 @@ func (client ZonesClient) CreateOrUpdateResponder(resp *http.Response) (result Z
return
}
// Delete deletes a DNS zone. WARNING: All DNS records in the zone will also
// be deleted. This operation cannot be undone. This method may poll for
// completion. Polling can be canceled by passing the cancel channel
// argument. The channel will be used to cancel polling and any outstanding
// HTTP requests.
// Delete deletes a DNS zone. WARNING: All DNS records in the zone will also be
// deleted. This operation cannot be undone. This method may poll for
// completion. Polling can be canceled by passing the cancel channel argument.
// The channel will be used to cancel polling and any outstanding HTTP
// requests.
//
// resourceGroupName is the name of the resource group. zoneName is the name
// of the DNS zone (without a terminating dot). ifMatch is the etag of the
// DNS zone. Omit this value to always delete the current zone. Specify the
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot). ifMatch is the etag of the DNS
// zone. Omit this value to always delete the current zone. Specify the
// last-seen etag value to prevent accidentally deleting any concurrent
// changes.
func (client ZonesClient) Delete(resourceGroupName string, zoneName string, ifMatch string, cancel <-chan struct{}) (result autorest.Response, err error) {
@ -165,8 +158,9 @@ func (client ZonesClient) DeletePreparer(resourceGroupName string, zoneName stri
"zoneName": autorest.Encode("path", zoneName),
}
const APIVersion = "2016-04-01"
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
"api-version": APIVersion,
}
preparer := autorest.CreatePreparer(
@ -204,8 +198,8 @@ func (client ZonesClient) DeleteResponder(resp *http.Response) (result autorest.
// Get gets a DNS zone. Retrieves the zone properties, but not the record sets
// within the zone.
//
// resourceGroupName is the name of the resource group. zoneName is the name
// of the DNS zone (without a terminating dot).
// resourceGroupName is the name of the resource group. zoneName is the name of
// the DNS zone (without a terminating dot).
func (client ZonesClient) Get(resourceGroupName string, zoneName string) (result Zone, err error) {
req, err := client.GetPreparer(resourceGroupName, zoneName)
if err != nil {
@ -234,8 +228,9 @@ func (client ZonesClient) GetPreparer(resourceGroupName string, zoneName string)
"zoneName": autorest.Encode("path", zoneName),
}
const APIVersion = "2016-04-01"
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
"api-version": APIVersion,
}
preparer := autorest.CreatePreparer(
@ -295,8 +290,9 @@ func (client ZonesClient) ListPreparer(top *int32) (*http.Request, error) {
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
const APIVersion = "2016-04-01"
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
"api-version": APIVersion,
}
if top != nil {
queryParameters["$top"] = autorest.Encode("query", *top)
@ -356,8 +352,8 @@ func (client ZonesClient) ListNextResults(lastResults ZoneListResult) (result Zo
// ListByResourceGroup lists the DNS zones within a resource group.
//
// resourceGroupName is the name of the resource group. top is the maximum
// number of record sets to return. If not specified, returns up to 100
// record sets.
// number of record sets to return. If not specified, returns up to 100 record
// sets.
func (client ZonesClient) ListByResourceGroup(resourceGroupName string, top *int32) (result ZoneListResult, err error) {
req, err := client.ListByResourceGroupPreparer(resourceGroupName, top)
if err != nil {
@ -385,8 +381,9 @@ func (client ZonesClient) ListByResourceGroupPreparer(resourceGroupName string,
"subscriptionId": autorest.Encode("path", client.SubscriptionID),
}
const APIVersion = "2016-04-01"
queryParameters := map[string]interface{}{
"api-version": client.APIVersion,
"api-version": APIVersion,
}
if top != nil {
queryParameters["$top"] = autorest.Encode("query", *top)

View file

@ -16,6 +16,7 @@ and Responding. A typical pattern is:
DoRetryForAttempts(5, time.Second))
err = Respond(resp,
ByDiscardingBody(),
ByClosing())
Each phase relies on decorators to modify and / or manage processing. Decorators may first modify

View file

@ -3,12 +3,13 @@ package azure
import (
"bytes"
"fmt"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/date"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/date"
)
const (

View file

@ -35,6 +35,7 @@ type Environment struct {
ServiceBusEndpointSuffix string `json:"serviceBusEndpointSuffix"`
ServiceManagementVMDNSSuffix string `json:"serviceManagementVMDNSSuffix"`
ResourceManagerVMDNSSuffix string `json:"resourceManagerVMDNSSuffix"`
ContainerRegistryDNSSuffix string `json:"containerRegistryDNSSuffix"`
}
var (
@ -56,6 +57,7 @@ var (
ServiceBusEndpointSuffix: "servicebus.azure.com",
ServiceManagementVMDNSSuffix: "cloudapp.net",
ResourceManagerVMDNSSuffix: "cloudapp.azure.com",
ContainerRegistryDNSSuffix: "azurecr.io",
}
// USGovernmentCloud is the cloud environment for the US Government
@ -76,6 +78,7 @@ var (
ServiceBusEndpointSuffix: "servicebus.usgovcloudapi.net",
ServiceManagementVMDNSSuffix: "usgovcloudapp.net",
ResourceManagerVMDNSSuffix: "cloudapp.windowsazure.us",
ContainerRegistryDNSSuffix: "azurecr.io",
}
// ChinaCloud is the cloud environment operated in China
@ -85,7 +88,7 @@ var (
PublishSettingsURL: "https://manage.chinacloudapi.com/publishsettings/index",
ServiceManagementEndpoint: "https://management.core.chinacloudapi.cn/",
ResourceManagerEndpoint: "https://management.chinacloudapi.cn/",
ActiveDirectoryEndpoint: "https://login.chinacloudapi.cn/?api-version=1.0",
ActiveDirectoryEndpoint: "https://login.chinacloudapi.cn/",
GalleryEndpoint: "https://gallery.chinacloudapi.cn/",
KeyVaultEndpoint: "https://vault.azure.cn/",
GraphEndpoint: "https://graph.chinacloudapi.cn/",
@ -96,6 +99,7 @@ var (
ServiceBusEndpointSuffix: "servicebus.chinacloudapi.net",
ServiceManagementVMDNSSuffix: "chinacloudapp.cn",
ResourceManagerVMDNSSuffix: "cloudapp.azure.cn",
ContainerRegistryDNSSuffix: "azurecr.io",
}
// GermanCloud is the cloud environment operated in Germany
@ -116,6 +120,7 @@ var (
ServiceBusEndpointSuffix: "servicebus.cloudapi.de",
ServiceManagementVMDNSSuffix: "azurecloudapp.de",
ResourceManagerVMDNSSuffix: "cloudapp.microsoftazure.de",
ContainerRegistryDNSSuffix: "azurecr.io",
}
)

View file

@ -8,6 +8,7 @@ import (
"log"
"net/http"
"net/http/cookiejar"
"runtime"
"time"
)
@ -22,13 +23,24 @@ const (
DefaultRetryAttempts = 3
)
var statusCodesForRetry = []int{
http.StatusRequestTimeout, // 408
http.StatusInternalServerError, // 500
http.StatusBadGateway, // 502
http.StatusServiceUnavailable, // 503
http.StatusGatewayTimeout, // 504
}
var (
// defaultUserAgent builds a string containing the Go version, system archityecture and OS,
// and the go-autorest version.
defaultUserAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s",
runtime.Version(),
runtime.GOARCH,
runtime.GOOS,
Version(),
)
statusCodesForRetry = []int{
http.StatusRequestTimeout, // 408
http.StatusInternalServerError, // 500
http.StatusBadGateway, // 502
http.StatusServiceUnavailable, // 503
http.StatusGatewayTimeout, // 504
}
)
const (
requestFormat = `HTTP Request Begin ===================================================
@ -140,13 +152,24 @@ type Client struct {
// NewClientWithUserAgent returns an instance of a Client with the UserAgent set to the passed
// string.
func NewClientWithUserAgent(ua string) Client {
return Client{
c := Client{
PollingDelay: DefaultPollingDelay,
PollingDuration: DefaultPollingDuration,
RetryAttempts: DefaultRetryAttempts,
RetryDuration: 30 * time.Second,
UserAgent: ua,
UserAgent: defaultUserAgent,
}
c.AddToUserAgent(ua)
return c
}
// AddToUserAgent adds an extension to the current user agent
func (c *Client) AddToUserAgent(extension string) error {
if extension != "" {
c.UserAgent = fmt.Sprintf("%s %s", c.UserAgent, extension)
return nil
}
return fmt.Errorf("Extension was empty, User Agent stayed as %s", c.UserAgent)
}
// Do implements the Sender interface by invoking the active Sender after applying authorization.

View file

@ -183,6 +183,16 @@ func WithBaseURL(baseURL string) PrepareDecorator {
}
}
// WithCustomBaseURL returns a PrepareDecorator that replaces brace-enclosed keys within the
// request base URL (i.e., http.Request.URL) with the corresponding values from the passed map.
func WithCustomBaseURL(baseURL string, urlParameters map[string]interface{}) PrepareDecorator {
parameters := ensureValueStrings(urlParameters)
for key, value := range parameters {
baseURL = strings.Replace(baseURL, "{"+key+"}", value, -1)
}
return WithBaseURL(baseURL)
}
// WithFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) into the
// http.Request body.
func WithFormData(v url.Values) PrepareDecorator {

View file

@ -5,6 +5,7 @@ import (
"encoding/json"
"encoding/xml"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
@ -87,6 +88,24 @@ func ByCopying(b *bytes.Buffer) RespondDecorator {
}
}
// ByDiscardingBody returns a RespondDecorator that first invokes the passed Responder after which
// it copies the remaining bytes (if any) in the response body to ioutil.Discard. Since the passed
// Responder is invoked prior to discarding the response body, the decorator may occur anywhere
// within the set.
func ByDiscardingBody() RespondDecorator {
return func(r Responder) Responder {
return ResponderFunc(func(resp *http.Response) error {
err := r.Respond(resp)
if err == nil && resp != nil && resp.Body != nil {
if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
return fmt.Errorf("Error discarding the response body: %v", err)
}
}
return err
})
}
}
// ByClosing returns a RespondDecorator that first invokes the passed Responder after which it
// closes the response body. Since the passed Responder is invoked prior to closing the response
// body, the decorator may occur anywhere within the set.
@ -128,6 +147,8 @@ func ByUnmarshallingJSON(v interface{}) RespondDecorator {
err := r.Respond(resp)
if err == nil {
b, errInner := ioutil.ReadAll(resp.Body)
// Some responses might include a BOM, remove for successful unmarshalling
b = bytes.TrimPrefix(b, []byte("\xef\xbb\xbf"))
if errInner != nil {
err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
} else if len(strings.Trim(string(b), " ")) > 0 {

View file

@ -73,7 +73,7 @@ func SendWithSender(s Sender, r *http.Request, decorators ...SendDecorator) (*ht
func AfterDelay(d time.Duration) SendDecorator {
return func(s Sender) Sender {
return SenderFunc(func(r *http.Request) (*http.Response, error) {
if !DelayForBackoff(d, 1, r.Cancel) {
if !DelayForBackoff(d, 0, r.Cancel) {
return nil, fmt.Errorf("autorest: AfterDelay canceled before full delay")
}
return s.Do(r)
@ -97,7 +97,7 @@ func DoCloseIfError() SendDecorator {
return SenderFunc(func(r *http.Request) (*http.Response, error) {
resp, err := s.Do(r)
if err != nil {
Respond(resp, ByClosing())
Respond(resp, ByDiscardingBody(), ByClosing())
}
return resp, err
})
@ -156,6 +156,7 @@ func DoPollForStatusCodes(duration time.Duration, delay time.Duration, codes ...
for err == nil && ResponseHasStatusCode(resp, codes...) {
Respond(resp,
ByDiscardingBody(),
ByClosing())
resp, err = SendWithSender(s, r,
AfterDelay(GetRetryAfter(resp, delay)))
@ -257,6 +258,8 @@ func WithLogging(logger *log.Logger) SendDecorator {
// passed attempt (i.e., an exponential backoff delay). Backoff duration is in seconds and can set
// to zero for no delay. The delay may be canceled by closing the passed channel. If terminated early,
// returns false.
// Note: Passing attempt 1 will result in doubling "backoff" duration. Treat this as a zero-based attempt
// count.
func DelayForBackoff(backoff time.Duration, attempt int, cancel <-chan struct{}) bool {
select {
case <-time.After(time.Duration(backoff.Seconds()*math.Pow(2, float64(attempt))) * time.Second):

View file

@ -1,373 +0,0 @@
/*
Package validation provides methods for validating parameter value using reflection.
*/
package validation
import (
"fmt"
"reflect"
"regexp"
"strings"
)
// Constraint stores constraint name, target field name
// Rule and chain validations.
type Constraint struct {
// Target field name for validation.
Target string
// Constraint name e.g. minLength, MaxLength, Pattern, etc.
Name string
// Rule for constraint e.g. greater than 10, less than 5 etc.
Rule interface{}
// Chain Validations for struct type
Chain []Constraint
}
// Validation stores parameter-wise validation.
type Validation struct {
TargetValue interface{}
Constraints []Constraint
}
// Constraint list
const (
Empty = "Empty"
Null = "Null"
ReadOnly = "ReadOnly"
Pattern = "Pattern"
MaxLength = "MaxLength"
MinLength = "MinLength"
MaxItems = "MaxItems"
MinItems = "MinItems"
MultipleOf = "MultipleOf"
UniqueItems = "UniqueItems"
InclusiveMaximum = "InclusiveMaximum"
ExclusiveMaximum = "ExclusiveMaximum"
ExclusiveMinimum = "ExclusiveMinimum"
InclusiveMinimum = "InclusiveMinimum"
)
// Validate method validates constraints on parameter
// passed in validation array.
func Validate(m []Validation) error {
for _, item := range m {
v := reflect.ValueOf(item.TargetValue)
for _, constraint := range item.Constraints {
var err error
switch v.Kind() {
case reflect.Ptr:
err = validatePtr(v, constraint)
case reflect.String:
err = validateString(v, constraint)
case reflect.Struct:
err = validateStruct(v, constraint)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
err = validateInt(v, constraint)
case reflect.Float32, reflect.Float64:
err = validateFloat(v, constraint)
case reflect.Array, reflect.Slice, reflect.Map:
err = validateArrayMap(v, constraint)
default:
err = createError(v, constraint, fmt.Sprintf("unknown type %v", v.Kind()))
}
if err != nil {
return err
}
}
}
return nil
}
func validateStruct(x reflect.Value, v Constraint, name ...string) error {
//Get field name from target name which is in format a.b.c
s := strings.Split(v.Target, ".")
f := x.FieldByName(s[len(s)-1])
if isZero(f) {
return createError(x, v, fmt.Sprintf("field %q doesn't exist", v.Target))
}
if err := Validate([]Validation{
{
TargetValue: getInterfaceValue(f),
Constraints: []Constraint{v},
},
}); err != nil {
return err
}
return nil
}
func validatePtr(x reflect.Value, v Constraint) error {
if v.Name == ReadOnly {
if !x.IsNil() {
return createError(x.Elem(), v, "readonly parameter; must send as nil or empty in request")
}
return nil
}
if x.IsNil() {
return checkNil(x, v)
}
if v.Chain != nil {
return Validate([]Validation{
{
TargetValue: getInterfaceValue(x.Elem()),
Constraints: v.Chain,
},
})
}
return nil
}
func validateInt(x reflect.Value, v Constraint) error {
i := x.Int()
r, ok := v.Rule.(int)
if !ok {
return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule))
}
switch v.Name {
case MultipleOf:
if i%int64(r) != 0 {
return createError(x, v, fmt.Sprintf("value must be a multiple of %v", r))
}
case ExclusiveMinimum:
if i <= int64(r) {
return createError(x, v, fmt.Sprintf("value must be greater than %v", r))
}
case ExclusiveMaximum:
if i >= int64(r) {
return createError(x, v, fmt.Sprintf("value must be less than %v", r))
}
case InclusiveMinimum:
if i < int64(r) {
return createError(x, v, fmt.Sprintf("value must be greater than or equal to %v", r))
}
case InclusiveMaximum:
if i > int64(r) {
return createError(x, v, fmt.Sprintf("value must be less than or equal to %v", r))
}
default:
return createError(x, v, fmt.Sprintf("constraint %v is not applicable for type integer", v.Name))
}
return nil
}
func validateFloat(x reflect.Value, v Constraint) error {
f := x.Float()
r, ok := v.Rule.(float64)
if !ok {
return createError(x, v, fmt.Sprintf("rule must be float value for %v constraint; got: %v", v.Name, v.Rule))
}
switch v.Name {
case ExclusiveMinimum:
if f <= r {
return createError(x, v, fmt.Sprintf("value must be greater than %v", r))
}
case ExclusiveMaximum:
if f >= r {
return createError(x, v, fmt.Sprintf("value must be less than %v", r))
}
case InclusiveMinimum:
if f < r {
return createError(x, v, fmt.Sprintf("value must be greater than or equal to %v", r))
}
case InclusiveMaximum:
if f > r {
return createError(x, v, fmt.Sprintf("value must be less than or equal to %v", r))
}
default:
return createError(x, v, fmt.Sprintf("constraint %s is not applicable for type float", v.Name))
}
return nil
}
func validateString(x reflect.Value, v Constraint) error {
s := x.String()
switch v.Name {
case Empty:
if len(s) == 0 {
return checkEmpty(x, v)
}
case Pattern:
reg, err := regexp.Compile(v.Rule.(string))
if err != nil {
return createError(x, v, err.Error())
}
if !reg.MatchString(s) {
return createError(x, v, fmt.Sprintf("value doesn't match pattern %v", v.Rule))
}
case MaxLength:
if _, ok := v.Rule.(int); !ok {
return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule))
}
if len(s) > v.Rule.(int) {
return createError(x, v, fmt.Sprintf("value length must be less than %v", v.Rule))
}
case MinLength:
if _, ok := v.Rule.(int); !ok {
return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule))
}
if len(s) < v.Rule.(int) {
return createError(x, v, fmt.Sprintf("value length must be greater than %v", v.Rule))
}
case ReadOnly:
if len(s) > 0 {
return createError(reflect.ValueOf(s), v, "readonly parameter; must send as nil or empty in request")
}
default:
return createError(x, v, fmt.Sprintf("constraint %s is not applicable to string type", v.Name))
}
if v.Chain != nil {
return Validate([]Validation{
{
TargetValue: getInterfaceValue(x),
Constraints: v.Chain,
},
})
}
return nil
}
func validateArrayMap(x reflect.Value, v Constraint) error {
switch v.Name {
case Null:
if x.IsNil() {
return checkNil(x, v)
}
case Empty:
if x.IsNil() || x.Len() == 0 {
return checkEmpty(x, v)
}
case MaxItems:
if _, ok := v.Rule.(int); !ok {
return createError(x, v, fmt.Sprintf("rule must be integer for %v constraint; got: %v", v.Name, v.Rule))
}
if x.Len() > v.Rule.(int) {
return createError(x, v, fmt.Sprintf("maximum item limit is %v; got: %v", v.Rule, x.Len()))
}
case MinItems:
if _, ok := v.Rule.(int); !ok {
return createError(x, v, fmt.Sprintf("rule must be integer for %v constraint; got: %v", v.Name, v.Rule))
}
if x.Len() < v.Rule.(int) {
return createError(x, v, fmt.Sprintf("minimum item limit is %v; got: %v", v.Rule, x.Len()))
}
case UniqueItems:
if x.Kind() == reflect.Array || x.Kind() == reflect.Slice {
if !checkForUniqueInArray(x) {
return createError(x, v, fmt.Sprintf("all items in parameter %q must be unique; got:%v", v.Target, x))
}
} else if x.Kind() == reflect.Map {
if !checkForUniqueInMap(x) {
return createError(x, v, fmt.Sprintf("all items in parameter %q must be unique; got:%v", v.Target, x))
}
} else {
return createError(x, v, fmt.Sprintf("type must be array, slice or map for constraint %v; got: %v", v.Name, x.Kind()))
}
case ReadOnly:
if x.Len() != 0 {
return createError(x, v, "readonly parameter; must send as nil or empty in request")
}
default:
return createError(x, v, fmt.Sprintf("constraint %v is not applicable to array, slice and map type", v.Name))
}
if v.Chain != nil {
return Validate([]Validation{
{
TargetValue: getInterfaceValue(x),
Constraints: v.Chain,
},
})
}
return nil
}
func checkNil(x reflect.Value, v Constraint) error {
if _, ok := v.Rule.(bool); !ok {
return createError(x, v, fmt.Sprintf("rule must be bool value for %v constraint; got: %v", v.Name, v.Rule))
}
if v.Rule.(bool) {
return createError(x, v, "value can not be null; required parameter")
}
return nil
}
func checkEmpty(x reflect.Value, v Constraint) error {
if _, ok := v.Rule.(bool); !ok {
return createError(x, v, fmt.Sprintf("rule must be bool value for %v constraint; got: %v", v.Name, v.Rule))
}
if v.Rule.(bool) {
return createError(x, v, "value can not be null or empty; required parameter")
}
return nil
}
func checkForUniqueInArray(x reflect.Value) bool {
if x == reflect.Zero(reflect.TypeOf(x)) || x.Len() == 0 {
return false
}
arrOfInterface := make([]interface{}, x.Len())
for i := 0; i < x.Len(); i++ {
arrOfInterface[i] = x.Index(i).Interface()
}
m := make(map[interface{}]bool)
for _, val := range arrOfInterface {
if m[val] {
return false
}
m[val] = true
}
return true
}
func checkForUniqueInMap(x reflect.Value) bool {
if x == reflect.Zero(reflect.TypeOf(x)) || x.Len() == 0 {
return false
}
mapOfInterface := make(map[interface{}]interface{}, x.Len())
keys := x.MapKeys()
for _, k := range keys {
mapOfInterface[k.Interface()] = x.MapIndex(k).Interface()
}
m := make(map[interface{}]bool)
for _, val := range mapOfInterface {
if m[val] {
return false
}
m[val] = true
}
return true
}
func getInterfaceValue(x reflect.Value) interface{} {
if x.Kind() == reflect.Invalid {
return nil
}
return x.Interface()
}
func isZero(x interface{}) bool {
return x == reflect.Zero(reflect.TypeOf(x)).Interface()
}
func createError(x reflect.Value, v Constraint, err string) error {
return fmt.Errorf("autorest/validation: validation failed: parameter=%s constraint=%s value=%#v details: %s",
v.Target, v.Name, getInterfaceValue(x), err)
}
// NewErrorWithValidationError appends package type and method name in
// validation error.
func NewErrorWithValidationError(err error, packageType, method string) error {
return fmt.Errorf("%s#%s: Invalid input: %v", packageType, method, err)
}

View file

@ -2,17 +2,28 @@ package autorest
import (
"fmt"
"strings"
"sync"
)
const (
major = "7"
minor = "0"
patch = "0"
tag = ""
semVerFormat = "%s.%s.%s%s"
major = 7
minor = 3
patch = 1
tag = ""
)
var versionLock sync.Once
var version string
// Version returns the semantic version (see http://semver.org).
func Version() string {
return fmt.Sprintf(semVerFormat, major, minor, patch, tag)
versionLock.Do(func() {
version = fmt.Sprintf("v%d.%d.%d", major, minor, patch)
if trimmed := strings.TrimPrefix(tag, "-"); trimmed != "" {
version = fmt.Sprintf("%s-%s", version, trimmed)
}
})
return version
}

21
vendor/github.com/dnsimple/dnsimple-go/LICENSE.txt generated vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2017 Aetrion LLC dba DNSimple
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,44 @@
package dnsimple
import (
)
type AccountsService struct {
client *Client
}
// Account represents a DNSimple account.
type Account struct {
ID int `json:"id,omitempty"`
Email string `json:"email,omitempty"`
PlanIdentifier string `json:"plan_identifier,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
// accountsResponse represents a response from an API method that returns a collection of Account struct.
type accountsResponse struct {
Response
Data []Account `json:"data"`
}
// ListAccounts list the accounts for an user.
//
// See https://developer.dnsimple.com/v2/accounts/#list
func (s *AccountsService) ListAccounts(options *ListOptions) (*accountsResponse, error) {
path := versioned("/accounts")
accountsResponse := &accountsResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, accountsResponse)
if err != nil {
return accountsResponse, err
}
accountsResponse.HttpResponse = resp
return accountsResponse, nil
}

View file

@ -0,0 +1,68 @@
package dnsimple
import (
"encoding/base64"
)
const (
httpHeaderDomainToken = "X-DNSimple-Domain-Token"
httpHeaderApiToken = "X-DNSimple-Token"
httpHeaderAuthorization = "Authorization"
)
// Provides credentials that can be used for authenticating with DNSimple.
//
// See https://developer.dnsimple.com/v2/#authentication
type Credentials interface {
// Returns the HTTP headers that should be set
// to authenticate the HTTP Request.
Headers() map[string]string
}
// Domain token authentication
type domainTokenCredentials struct {
domainToken string
}
// NewDomainTokenCredentials construct Credentials using the DNSimple Domain Token method.
func NewDomainTokenCredentials(domainToken string) Credentials {
return &domainTokenCredentials{domainToken: domainToken}
}
func (c *domainTokenCredentials) Headers() map[string]string {
return map[string]string{httpHeaderDomainToken: c.domainToken}
}
// HTTP basic authentication
type httpBasicCredentials struct {
email string
password string
}
// NewHTTPBasicCredentials construct Credentials using HTTP Basic Auth.
func NewHTTPBasicCredentials(email, password string) Credentials {
return &httpBasicCredentials{email, password}
}
func (c *httpBasicCredentials) Headers() map[string]string {
return map[string]string{httpHeaderAuthorization: "Basic " + c.basicAuth(c.email, c.password)}
}
func (c *httpBasicCredentials) basicAuth(username, password string) string {
auth := username + ":" + password
return base64.StdEncoding.EncodeToString([]byte(auth))
}
// OAuth token authentication
type oauthTokenCredentials struct {
oauthToken string
}
// NewOauthTokenCredentials construct Credentials using the OAuth access token.
func NewOauthTokenCredentials(oauthToken string) Credentials {
return &oauthTokenCredentials{oauthToken: oauthToken}
}
func (c *oauthTokenCredentials) Headers() map[string]string {
return map[string]string{httpHeaderAuthorization: "Bearer " + c.oauthToken}
}

View file

@ -0,0 +1,134 @@
package dnsimple
import (
"fmt"
"strconv"
)
// CertificatesService handles communication with the certificate related
// methods of the DNSimple API.
//
// See https://developer.dnsimple.com/v2/domains/certificates
type CertificatesService struct {
client *Client
}
// Certificate represents a Certificate in DNSimple.
type Certificate struct {
ID int `json:"id,omitempty"`
DomainID int `json:"domain_id,omitempty"`
CommonName string `json:"common_name,omitempty"`
Years int `json:"years,omitempty"`
State string `json:"state,omitempty"`
AuthorityIdentifier string `json:"authority_identifier,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
ExpiresOn string `json:"expires_on,omitempty"`
CertificateRequest string `json:"csr,omitempty"`
}
// CertificateBundle represents a container for all the PEM-encoded X509 certificate entities,
// such as the private key, the server certificate and the intermediate chain.
type CertificateBundle struct {
// CertificateRequest string `json:"csr,omitempty"`
PrivateKey string `json:"private_key,omitempty"`
ServerCertificate string `json:"server,omitempty"`
RootCertificate string `json:"root,omitempty"`
IntermediateCertificates []string `json:"chain,omitempty"`
}
func certificatePath(accountID, domainIdentifier, certificateID string) (path string) {
path = fmt.Sprintf("%v/certificates", domainPath(accountID, domainIdentifier))
if certificateID != "" {
path += fmt.Sprintf("/%v", certificateID)
}
return
}
// certificateResponse represents a response from an API method that returns a Certificate struct.
type certificateResponse struct {
Response
Data *Certificate `json:"data"`
}
// certificateBundleResponse represents a response from an API method that returns a CertificatBundle struct.
type certificateBundleResponse struct {
Response
Data *CertificateBundle `json:"data"`
}
// certificatesResponse represents a response from an API method that returns a collection of Certificate struct.
type certificatesResponse struct {
Response
Data []Certificate `json:"data"`
}
// ListCertificates list the certificates for a domain.
//
// See https://developer.dnsimple.com/v2/domains/certificates#list
func (s *CertificatesService) ListCertificates(accountID, domainIdentifier string, options *ListOptions) (*certificatesResponse, error) {
path := versioned(certificatePath(accountID, domainIdentifier, ""))
certificatesResponse := &certificatesResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, certificatesResponse)
if err != nil {
return certificatesResponse, err
}
certificatesResponse.HttpResponse = resp
return certificatesResponse, nil
}
// GetCertificate fetches the certificate.
//
// See https://developer.dnsimple.com/v2/domains/certificates#get
func (s *CertificatesService) GetCertificate(accountID, domainIdentifier string, certificateID int) (*certificateResponse, error) {
path := versioned(certificatePath(accountID, domainIdentifier, strconv.Itoa(certificateID)))
certificateResponse := &certificateResponse{}
resp, err := s.client.get(path, certificateResponse)
if err != nil {
return nil, err
}
certificateResponse.HttpResponse = resp
return certificateResponse, nil
}
// DownloadCertificate download the issued server certificate,
// as well the root certificate and the intermediate chain.
//
// See https://developer.dnsimple.com/v2/domains/certificates#download
func (s *CertificatesService) DownloadCertificate(accountID, domainIdentifier string, certificateID int) (*certificateBundleResponse, error) {
path := versioned(certificatePath(accountID, domainIdentifier, strconv.Itoa(certificateID)) + "/download")
certificateBundleResponse := &certificateBundleResponse{}
resp, err := s.client.get(path, certificateBundleResponse)
if err != nil {
return nil, err
}
certificateBundleResponse.HttpResponse = resp
return certificateBundleResponse, nil
}
// GetCertificatePrivateKey fetches the certificate private key.
//
// See https://developer.dnsimple.com/v2/domains/certificates#get-private-key
func (s *CertificatesService) GetCertificatePrivateKey(accountID, domainIdentifier string, certificateID int) (*certificateBundleResponse, error) {
path := versioned(certificatePath(accountID, domainIdentifier, strconv.Itoa(certificateID)) + "/private_key")
certificateBundleResponse := &certificateBundleResponse{}
resp, err := s.client.get(path, certificateBundleResponse)
if err != nil {
return nil, err
}
certificateBundleResponse.HttpResponse = resp
return certificateBundleResponse, nil
}

View file

@ -0,0 +1,140 @@
package dnsimple
import (
"fmt"
)
// ContactsService handles communication with the contact related
// methods of the DNSimple API.
//
// See https://developer.dnsimple.com/v2/contacts/
type ContactsService struct {
client *Client
}
// Contact represents a Contact in DNSimple.
type Contact struct {
ID int `json:"id,omitempty"`
AccountID int `json:"account_id,omitempty"`
Label string `json:"label,omitempty"`
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
JobTitle string `json:"job_title,omitempty"`
Organization string `json:"organization_name,omitempty"`
Address1 string `json:"address1,omitempty"`
Address2 string `json:"address2,omitempty"`
City string `json:"city,omitempty"`
StateProvince string `json:"state_province,omitempty"`
PostalCode string `json:"postal_code,omitempty"`
Country string `json:"country,omitempty"`
Phone string `json:"phone,omitempty"`
Fax string `json:"fax,omitempty"`
Email string `json:"email,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
func contactPath(accountID string, contactID int) (path string) {
path = fmt.Sprintf("/%v/contacts", accountID)
if contactID != 0 {
path += fmt.Sprintf("/%v", contactID)
}
return
}
// contactResponse represents a response from an API method that returns a Contact struct.
type contactResponse struct {
Response
Data *Contact `json:"data"`
}
// contactsResponse represents a response from an API method that returns a collection of Contact struct.
type contactsResponse struct {
Response
Data []Contact `json:"data"`
}
// ListContacts list the contacts for an account.
//
// See https://developer.dnsimple.com/v2/contacts/#list
func (s *ContactsService) ListContacts(accountID string, options *ListOptions) (*contactsResponse, error) {
path := versioned(contactPath(accountID, 0))
contactsResponse := &contactsResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, contactsResponse)
if err != nil {
return contactsResponse, err
}
contactsResponse.HttpResponse = resp
return contactsResponse, nil
}
// CreateContact creates a new contact.
//
// See https://developer.dnsimple.com/v2/contacts/#create
func (s *ContactsService) CreateContact(accountID string, contactAttributes Contact) (*contactResponse, error) {
path := versioned(contactPath(accountID, 0))
contactResponse := &contactResponse{}
resp, err := s.client.post(path, contactAttributes, contactResponse)
if err != nil {
return nil, err
}
contactResponse.HttpResponse = resp
return contactResponse, nil
}
// GetContact fetches a contact.
//
// See https://developer.dnsimple.com/v2/contacts/#get
func (s *ContactsService) GetContact(accountID string, contactID int) (*contactResponse, error) {
path := versioned(contactPath(accountID, contactID))
contactResponse := &contactResponse{}
resp, err := s.client.get(path, contactResponse)
if err != nil {
return nil, err
}
contactResponse.HttpResponse = resp
return contactResponse, nil
}
// UpdateContact updates a contact.
//
// See https://developer.dnsimple.com/v2/contacts/#update
func (s *ContactsService) UpdateContact(accountID string, contactID int, contactAttributes Contact) (*contactResponse, error) {
path := versioned(contactPath(accountID, contactID))
contactResponse := &contactResponse{}
resp, err := s.client.patch(path, contactAttributes, contactResponse)
if err != nil {
return nil, err
}
contactResponse.HttpResponse = resp
return contactResponse, nil
}
// DeleteContact PERMANENTLY deletes a contact from the account.
//
// See https://developer.dnsimple.com/v2/contacts/#delete
func (s *ContactsService) DeleteContact(accountID string, contactID int) (*contactResponse, error) {
path := versioned(contactPath(accountID, contactID))
contactResponse := &contactResponse{}
resp, err := s.client.delete(path, nil, nil)
if err != nil {
return nil, err
}
contactResponse.HttpResponse = resp
return contactResponse, nil
}

View file

@ -0,0 +1,341 @@
// Package dnsimple provides a client for the DNSimple API.
// In order to use this package you will need a DNSimple account.
package dnsimple
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
"time"
"github.com/google/go-querystring/query"
)
const (
// Version identifies the current library version.
// This is a pro-forma convention given that Go dependencies
// tends to be fetched directly from the repo.
// It is also used in the user-agent identify the client.
Version = "0.14.0"
// defaultBaseURL to the DNSimple production API.
defaultBaseURL = "https://api.dnsimple.com"
// userAgent represents the default user agent used
// when no other user agent is set.
defaultUserAgent = "dnsimple-go/" + Version
apiVersion = "v2"
)
// Client represents a client to the DNSimple API.
type Client struct {
// HttpClient is the underlying HTTP client
// used to communicate with the API.
HttpClient *http.Client
// Credentials used for accessing the DNSimple API
Credentials Credentials
// BaseURL for API requests.
// Defaults to the public DNSimple API, but can be set to a different endpoint (e.g. the sandbox).
BaseURL string
// UserAgent used when communicating with the DNSimple API.
UserAgent string
// Services used for talking to different parts of the DNSimple API.
Identity *IdentityService
Accounts *AccountsService
Certificates *CertificatesService
Contacts *ContactsService
Domains *DomainsService
Oauth *OauthService
Registrar *RegistrarService
Services *ServicesService
Templates *TemplatesService
Tlds *TldsService
VanityNameServers *VanityNameServersService
Webhooks *WebhooksService
Zones *ZonesService
// Set to true to output debugging logs during API calls
Debug bool
}
// ListOptions contains the common options you can pass to a List method
// in order to control parameters such as paginations and page number.
type ListOptions struct {
// The page to return
Page int `url:"page,omitempty"`
// The number of entries to return per page
PerPage int `url:"per_page,omitempty"`
// The order criteria to sort the results.
// The value is a comma-separated list of field[:direction],
// eg. name | name:desc | name:desc,expiration:desc
Sort string `url:"sort,omitempty"`
}
// NewClient returns a new DNSimple API client using the given credentials.
func NewClient(credentials Credentials) *Client {
c := &Client{Credentials: credentials, HttpClient: &http.Client{}, BaseURL: defaultBaseURL}
c.Identity = &IdentityService{client: c}
c.Accounts = &AccountsService{client: c}
c.Certificates = &CertificatesService{client: c}
c.Contacts = &ContactsService{client: c}
c.Domains = &DomainsService{client: c}
c.Oauth = &OauthService{client: c}
c.Registrar = &RegistrarService{client: c}
c.Services = &ServicesService{client: c}
c.Templates = &TemplatesService{client: c}
c.Tlds = &TldsService{client: c}
c.VanityNameServers = &VanityNameServersService{client: c}
c.Webhooks = &WebhooksService{client: c}
c.Zones = &ZonesService{client: c}
return c
}
// NewRequest creates an API request.
// The path is expected to be a relative path and will be resolved
// according to the BaseURL of the Client. Paths should always be specified without a preceding slash.
func (c *Client) NewRequest(method, path string, payload interface{}) (*http.Request, error) {
url := c.BaseURL + path
body := new(bytes.Buffer)
if payload != nil {
err := json.NewEncoder(body).Encode(payload)
if err != nil {
return nil, err
}
}
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
req.Header.Add("User-Agent", formatUserAgent(c.UserAgent))
for key, value := range c.Credentials.Headers() {
req.Header.Add(key, value)
}
return req, nil
}
// formatUserAgent builds the final user agent to use for HTTP requests.
//
// If no custom user agent is provided, the default user agent is used.
//
// dnsimple-go/1.0
//
// If a custom user agent is provided, the final user agent is the combination of the custom user agent
// prepended by the default user agent.
//
// dnsimple-go/1.0 customAgentFlag
//
func formatUserAgent(customUserAgent string) string {
if customUserAgent == "" {
return defaultUserAgent
}
return fmt.Sprintf("%s %s", defaultUserAgent, customUserAgent)
}
func versioned(path string) string {
return fmt.Sprintf("/%s/%s", apiVersion, strings.Trim(path, "/"))
}
func (c *Client) get(path string, obj interface{}) (*http.Response, error) {
req, err := c.NewRequest("GET", path, nil)
if err != nil {
return nil, err
}
return c.Do(req, obj)
}
func (c *Client) post(path string, payload, obj interface{}) (*http.Response, error) {
req, err := c.NewRequest("POST", path, payload)
if err != nil {
return nil, err
}
return c.Do(req, obj)
}
func (c *Client) put(path string, payload, obj interface{}) (*http.Response, error) {
req, err := c.NewRequest("PUT", path, payload)
if err != nil {
return nil, err
}
return c.Do(req, obj)
}
func (c *Client) patch(path string, payload, obj interface{}) (*http.Response, error) {
req, err := c.NewRequest("PATCH", path, payload)
if err != nil {
return nil, err
}
return c.Do(req, obj)
}
func (c *Client) delete(path string, payload interface{}, obj interface{}) (*http.Response, error) {
req, err := c.NewRequest("DELETE", path, payload)
if err != nil {
return nil, err
}
return c.Do(req, obj)
}
// Do sends an API request and returns the API response.
//
// The API response is JSON decoded and stored in the value pointed by obj,
// or returned as an error if an API error has occurred.
// If obj implements the io.Writer interface, the raw response body will be written to obj,
// without attempting to decode it.
func (c *Client) Do(req *http.Request, obj interface{}) (*http.Response, error) {
if c.Debug {
log.Printf("Executing request (%v): %#v", req.URL, req)
}
resp, err := c.HttpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if c.Debug {
log.Printf("Response received: %#v", resp)
}
err = CheckResponse(resp)
if err != nil {
return resp, err
}
// If obj implements the io.Writer,
// the response body is decoded into v.
if obj != nil {
if w, ok := obj.(io.Writer); ok {
io.Copy(w, resp.Body)
} else {
err = json.NewDecoder(resp.Body).Decode(obj)
}
}
return resp, err
}
// A Response represents an API response.
type Response struct {
// HTTP response
HttpResponse *http.Response
// If the response is paginated, the Pagination will store them.
Pagination *Pagination `json:"pagination"`
}
// RateLimit returns the maximum amount of requests this account can send in an hour.
func (r *Response) RateLimit() int {
value, _ := strconv.Atoi(r.HttpResponse.Header.Get("X-RateLimit-Limit"))
return value
}
// RateLimitRemaining returns the remaining amount of requests this account can send within this hour window.
func (r *Response) RateLimitRemaining() int {
value, _ := strconv.Atoi(r.HttpResponse.Header.Get("X-RateLimit-Remaining"))
return value
}
// RateLimitReset returns when the throttling window will be reset for this account.
func (r *Response) RateLimitReset() time.Time {
value, _ := strconv.ParseInt(r.HttpResponse.Header.Get("X-RateLimit-Reset"), 10, 64)
return time.Unix(value, 0)
}
// If the response is paginated, Pagination represents the pagination information.
type Pagination struct {
CurrentPage int `json:"current_page"`
PerPage int `json:"per_page"`
TotalPages int `json:"total_pages"`
TotalEntries int `json:"total_entries"`
}
// An ErrorResponse represents an API response that generated an error.
type ErrorResponse struct {
Response
// human-readable message
Message string `json:"message"`
}
// Error implements the error interface.
func (r *ErrorResponse) Error() string {
return fmt.Sprintf("%v %v: %v %v",
r.HttpResponse.Request.Method, r.HttpResponse.Request.URL,
r.HttpResponse.StatusCode, r.Message)
}
// CheckResponse checks the API response for errors, and returns them if present.
// A response is considered an error if the status code is different than 2xx. Specific requests
// may have additional requirements, but this is sufficient in most of the cases.
func CheckResponse(resp *http.Response) error {
if code := resp.StatusCode; 200 <= code && code <= 299 {
return nil
}
errorResponse := &ErrorResponse{}
errorResponse.HttpResponse = resp
err := json.NewDecoder(resp.Body).Decode(errorResponse)
if err != nil {
return err
}
return errorResponse
}
// addOptions adds the parameters in opt as URL query parameters to s. opt
// must be a struct whose fields may contain "url" tags.
func addURLQueryOptions(path string, options interface{}) (string, error) {
opt := reflect.ValueOf(options)
// options is a pointer
// return if the value of the pointer is nil,
if opt.Kind() == reflect.Ptr && opt.IsNil() {
return path, nil
}
// append the options to the URL
u, err := url.Parse(path)
if err != nil {
return path, err
}
qs, err := query.Values(options)
if err != nil {
return path, err
}
uqs := u.Query()
for k, _ := range qs {
uqs.Set(k, qs.Get(k))
}
u.RawQuery = uqs.Encode()
return u.String(), nil
}

View file

@ -0,0 +1,146 @@
package dnsimple
import (
"fmt"
)
// DomainsService handles communication with the domain related
// methods of the DNSimple API.
//
// See https://developer.dnsimple.com/v2/domains/
type DomainsService struct {
client *Client
}
// Domain represents a domain in DNSimple.
type Domain struct {
ID int `json:"id,omitempty"`
AccountID int `json:"account_id,omitempty"`
RegistrantID int `json:"registrant_id,omitempty"`
Name string `json:"name,omitempty"`
UnicodeName string `json:"unicode_name,omitempty"`
Token string `json:"token,omitempty"`
State string `json:"state,omitempty"`
AutoRenew bool `json:"auto_renew,omitempty"`
PrivateWhois bool `json:"private_whois,omitempty"`
ExpiresOn string `json:"expires_on,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
func domainPath(accountID string, domainIdentifier string) (path string) {
path = fmt.Sprintf("/%v/domains", accountID)
if domainIdentifier != "" {
path += fmt.Sprintf("/%v", domainIdentifier)
}
return
}
// domainResponse represents a response from an API method that returns a Domain struct.
type domainResponse struct {
Response
Data *Domain `json:"data"`
}
// domainsResponse represents a response from an API method that returns a collection of Domain struct.
type domainsResponse struct {
Response
Data []Domain `json:"data"`
}
// DomainListOptions specifies the optional parameters you can provide
// to customize the DomainsService.ListDomains method.
type DomainListOptions struct {
// Select domains where the name contains given string.
NameLike string `url:"name_like,omitempty"`
// Select domains where the registrant matches given ID.
RegistrantID int `url:"registrant_id,omitempty"`
ListOptions
}
// ListDomains lists the domains for an account.
//
// See https://developer.dnsimple.com/v2/domains/#list
func (s *DomainsService) ListDomains(accountID string, options *DomainListOptions) (*domainsResponse, error) {
path := versioned(domainPath(accountID, ""))
domainsResponse := &domainsResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, domainsResponse)
if err != nil {
return nil, err
}
domainsResponse.HttpResponse = resp
return domainsResponse, nil
}
// CreateDomain creates a new domain in the account.
//
// See https://developer.dnsimple.com/v2/domains/#create
func (s *DomainsService) CreateDomain(accountID string, domainAttributes Domain) (*domainResponse, error) {
path := versioned(domainPath(accountID, ""))
domainResponse := &domainResponse{}
resp, err := s.client.post(path, domainAttributes, domainResponse)
if err != nil {
return nil, err
}
domainResponse.HttpResponse = resp
return domainResponse, nil
}
// GetDomain fetches a domain.
//
// See https://developer.dnsimple.com/v2/domains/#get
func (s *DomainsService) GetDomain(accountID string, domainIdentifier string) (*domainResponse, error) {
path := versioned(domainPath(accountID, domainIdentifier))
domainResponse := &domainResponse{}
resp, err := s.client.get(path, domainResponse)
if err != nil {
return nil, err
}
domainResponse.HttpResponse = resp
return domainResponse, nil
}
// DeleteDomain PERMANENTLY deletes a domain from the account.
//
// See https://developer.dnsimple.com/v2/domains/#delete
func (s *DomainsService) DeleteDomain(accountID string, domainIdentifier string) (*domainResponse, error) {
path := versioned(domainPath(accountID, domainIdentifier))
domainResponse := &domainResponse{}
resp, err := s.client.delete(path, nil, nil)
if err != nil {
return nil, err
}
domainResponse.HttpResponse = resp
return domainResponse, nil
}
// ResetDomainToken resets the domain token.
//
// See https://developer.dnsimple.com/v2/domains/#reset-token
func (s *DomainsService) ResetDomainToken(accountID string, domainIdentifier string) (*domainResponse, error) {
path := versioned(domainPath(accountID, domainIdentifier) + "/token")
domainResponse := &domainResponse{}
resp, err := s.client.post(path, nil, domainResponse)
if err != nil {
return nil, err
}
domainResponse.HttpResponse = resp
return domainResponse, nil
}

View file

@ -0,0 +1,96 @@
package dnsimple
import (
"fmt"
)
// Collaborator represents a Collaborator in DNSimple.
type Collaborator struct {
ID int `json:"id,omitempty"`
DomainID int `json:"domain_id,omitempty"`
DomainName string `json:"domain_name,omitempty"`
UserID int `json:"user_id,omitempty"`
UserEmail string `json:"user_email,omitempty"`
Invitation bool `json:"invitation,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
AcceptedAt string `json:"accepted_at,omitempty"`
}
func collaboratorPath(accountID, domainIdentifier, collaboratorID string) (path string) {
path = fmt.Sprintf("%v/collaborators", domainPath(accountID, domainIdentifier))
if collaboratorID != "" {
path += fmt.Sprintf("/%v", collaboratorID)
}
return
}
// CollaboratorAttributes represents Collaborator attributes for AddCollaborator operation.
type CollaboratorAttributes struct {
Email string `json:"email,omitempty"`
}
// collaboratorResponse represents a response from an API method that returns a Collaborator struct.
type collaboratorResponse struct {
Response
Data *Collaborator `json:"data"`
}
// collaboratorsResponse represents a response from an API method that returns a collection of Collaborator struct.
type collaboratorsResponse struct {
Response
Data []Collaborator `json:"data"`
}
// ListCollaborators list the collaborators for a domain.
//
// See https://developer.dnsimple.com/v2/domains/collaborators#list
func (s *DomainsService) ListCollaborators(accountID, domainIdentifier string, options *ListOptions) (*collaboratorsResponse, error) {
path := versioned(collaboratorPath(accountID, domainIdentifier, ""))
collaboratorsResponse := &collaboratorsResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, collaboratorsResponse)
if err != nil {
return collaboratorsResponse, err
}
collaboratorsResponse.HttpResponse = resp
return collaboratorsResponse, nil
}
// AddCollaborator adds a new collaborator to the domain in the account.
//
// See https://developer.dnsimple.com/v2/domains/collaborators#add
func (s *DomainsService) AddCollaborator(accountID string, domainIdentifier string, attributes CollaboratorAttributes) (*collaboratorResponse, error) {
path := versioned(collaboratorPath(accountID, domainIdentifier, ""))
collaboratorResponse := &collaboratorResponse{}
resp, err := s.client.post(path, attributes, collaboratorResponse)
if err != nil {
return nil, err
}
collaboratorResponse.HttpResponse = resp
return collaboratorResponse, nil
}
// RemoveCollaborator PERMANENTLY deletes a domain from the account.
//
// See https://developer.dnsimple.com/v2/domains/collaborators#add
func (s *DomainsService) RemoveCollaborator(accountID string, domainIdentifier string, collaboratorID string) (*collaboratorResponse, error) {
path := versioned(collaboratorPath(accountID, domainIdentifier, collaboratorID))
collaboratorResponse := &collaboratorResponse{}
resp, err := s.client.delete(path, nil, nil)
if err != nil {
return nil, err
}
collaboratorResponse.HttpResponse = resp
return collaboratorResponse, nil
}

View file

@ -0,0 +1,105 @@
package dnsimple
import "fmt"
// DelegationSignerRecord represents a delegation signer record for a domain in DNSimple.
type DelegationSignerRecord struct {
ID int `json:"id,omitempty"`
DomainID int `json:"domain_id,omitempty"`
Algorithm string `json:"algorithm"`
Digest string `json:"digest"`
DigestType string `json:"digest_type"`
Keytag string `json:"keytag"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
func delegationSignerRecordPath(accountID string, domainIdentifier string, dsRecordID int) (path string) {
path = fmt.Sprintf("%v/ds_records", domainPath(accountID, domainIdentifier))
if dsRecordID != 0 {
path += fmt.Sprintf("/%d", dsRecordID)
}
return
}
// delegationSignerRecordResponse represents a response from an API method that returns a DelegationSignerRecord struct.
type delegationSignerRecordResponse struct {
Response
Data *DelegationSignerRecord `json:"data"`
}
// delegationSignerRecordResponse represents a response from an API method that returns a DelegationSignerRecord struct.
type delegationSignerRecordsResponse struct {
Response
Data []DelegationSignerRecord `json:"data"`
}
// ListDelegationSignerRecords lists the delegation signer records for a domain.
//
// See https://developer.dnsimple.com/v2/domains/dnssec/#ds-record-list
func (s *DomainsService) ListDelegationSignerRecords(accountID string, domainIdentifier string, options *ListOptions) (*delegationSignerRecordsResponse, error) {
path := versioned(delegationSignerRecordPath(accountID, domainIdentifier, 0))
dsRecordsResponse := &delegationSignerRecordsResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, dsRecordsResponse)
if err != nil {
return nil, err
}
dsRecordsResponse.HttpResponse = resp
return dsRecordsResponse, nil
}
// CreateDelegationSignerRecord creates a new delegation signer record.
//
// See https://developer.dnsimple.com/v2/domains/dnssec/#ds-record-create
func (s *DomainsService) CreateDelegationSignerRecord(accountID string, domainIdentifier string, dsRecordAttributes DelegationSignerRecord) (*delegationSignerRecordResponse, error) {
path := versioned(delegationSignerRecordPath(accountID, domainIdentifier, 0))
dsRecordResponse := &delegationSignerRecordResponse{}
resp, err := s.client.post(path, dsRecordAttributes, dsRecordResponse)
if err != nil {
return nil, err
}
dsRecordResponse.HttpResponse = resp
return dsRecordResponse, nil
}
// GetDelegationSignerRecord fetches a delegation signer record.
//
// See https://developer.dnsimple.com/v2/domains/dnssec/#ds-record-get
func (s *DomainsService) GetDelegationSignerRecord(accountID string, domainIdentifier string, dsRecordID int) (*delegationSignerRecordResponse, error) {
path := versioned(delegationSignerRecordPath(accountID, domainIdentifier, dsRecordID))
dsRecordResponse := &delegationSignerRecordResponse{}
resp, err := s.client.get(path, dsRecordResponse)
if err != nil {
return nil, err
}
dsRecordResponse.HttpResponse = resp
return dsRecordResponse, nil
}
// DeleteDelegationSignerRecord PERMANENTLY deletes a delegation signer record
// from the domain.
//
// See https://developer.dnsimple.com/v2/domains/dnssec/#ds-record-delete
func (s *DomainsService) DeleteDelegationSignerRecord(accountID string, domainIdentifier string, dsRecordID int) (*delegationSignerRecordResponse, error) {
path := versioned(delegationSignerRecordPath(accountID, domainIdentifier, dsRecordID))
dsRecordResponse := &delegationSignerRecordResponse{}
resp, err := s.client.delete(path, nil, nil)
if err != nil {
return nil, err
}
dsRecordResponse.HttpResponse = resp
return dsRecordResponse, nil
}

View file

@ -0,0 +1,68 @@
package dnsimple
import "fmt"
// Dnssec represents the current DNSSEC settings for a domain in DNSimple.
type Dnssec struct {
Enabled bool `json:"enabled"`
}
func dnssecPath(accountID string, domainIdentifier string) (path string) {
path = fmt.Sprintf("%v/dnssec", domainPath(accountID, domainIdentifier))
return
}
// dnssecResponse represents a response from an API method that returns a Dnssec struct.
type dnssecResponse struct {
Response
Data *Dnssec `json:"data"`
}
// EnableDnssec enables DNSSEC on the domain.
//
// See https://developer.dnsimple.com/v2/domains/dnssec/#enable
func (s *DomainsService) EnableDnssec(accountID string, domainIdentifier string) (*dnssecResponse, error) {
path := versioned(dnssecPath(accountID, domainIdentifier))
dnssecResponse := &dnssecResponse{}
resp, err := s.client.post(path, dnssecResponse, nil)
if err != nil {
return nil, err
}
dnssecResponse.HttpResponse = resp
return dnssecResponse, nil
}
// DisableDnssec disables DNSSEC on the domain.
//
// See https://developer.dnsimple.com/v2/domains/dnssec/#disable
func (s *DomainsService) DisableDnssec(accountID string, domainIdentifier string) (*dnssecResponse, error) {
path := versioned(dnssecPath(accountID, domainIdentifier))
dnssecResponse := &dnssecResponse{}
resp, err := s.client.delete(path, dnssecResponse, nil)
if err != nil {
return nil, err
}
dnssecResponse.HttpResponse = resp
return dnssecResponse, nil
}
// GetDnssec retrieves the current status of DNSSEC on the domain.
//
// See https://developer.dnsimple.com/v2/domains/dnssec/#get
func (s *DomainsService) GetDnssec(accountID string, domainIdentifier string) (*dnssecResponse, error) {
path := versioned(dnssecPath(accountID, domainIdentifier))
dnssecResponse := &dnssecResponse{}
resp, err := s.client.get(path, dnssecResponse)
if err != nil {
return nil, err
}
dnssecResponse.HttpResponse = resp
return dnssecResponse, nil
}

View file

@ -0,0 +1,104 @@
package dnsimple
import (
"fmt"
)
// EmailForward represents an email forward in DNSimple.
type EmailForward struct {
ID int `json:"id,omitempty"`
DomainID int `json:"domain_id,omitempty"`
From string `json:"from,omitempty"`
To string `json:"to,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
func emailForwardPath(accountID string, domainIdentifier string, forwardID int) (path string) {
path = fmt.Sprintf("%v/email_forwards", domainPath(accountID, domainIdentifier))
if forwardID != 0 {
path += fmt.Sprintf("/%d", forwardID)
}
return
}
// emailForwardResponse represents a response from an API method that returns an EmailForward struct.
type emailForwardResponse struct {
Response
Data *EmailForward `json:"data"`
}
// emailForwardsResponse represents a response from an API method that returns a collection of EmailForward struct.
type emailForwardsResponse struct {
Response
Data []EmailForward `json:"data"`
}
// ListEmailForwards lists the email forwards for a domain.
//
// See https://developer.dnsimple.com/v2/domains/email-forwards/#list
func (s *DomainsService) ListEmailForwards(accountID string, domainIdentifier string, options *ListOptions) (*emailForwardsResponse, error) {
path := versioned(emailForwardPath(accountID, domainIdentifier , 0))
forwardsResponse := &emailForwardsResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, forwardsResponse)
if err != nil {
return nil, err
}
forwardsResponse.HttpResponse = resp
return forwardsResponse, nil
}
// CreateEmailForward creates a new email forward.
//
// See https://developer.dnsimple.com/v2/domains/email-forwards/#create
func (s *DomainsService) CreateEmailForward(accountID string, domainIdentifier string, forwardAttributes EmailForward) (*emailForwardResponse, error) {
path := versioned(emailForwardPath(accountID, domainIdentifier, 0))
forwardResponse := &emailForwardResponse{}
resp, err := s.client.post(path, forwardAttributes, forwardResponse)
if err != nil {
return nil, err
}
forwardResponse.HttpResponse = resp
return forwardResponse, nil
}
// GetEmailForward fetches an email forward.
//
// See https://developer.dnsimple.com/v2/domains/email-forwards/#get
func (s *DomainsService) GetEmailForward(accountID string, domainIdentifier string, forwardID int) (*emailForwardResponse, error) {
path := versioned(emailForwardPath(accountID, domainIdentifier, forwardID))
forwardResponse := &emailForwardResponse{}
resp, err := s.client.get(path, forwardResponse)
if err != nil {
return nil, err
}
forwardResponse.HttpResponse = resp
return forwardResponse, nil
}
// DeleteEmailForward PERMANENTLY deletes an email forward from the domain.
//
// See https://developer.dnsimple.com/v2/domains/email-forwards/#delete
func (s *DomainsService) DeleteEmailForward(accountID string, domainIdentifier string, forwardID int) (*emailForwardResponse, error) {
path := versioned(emailForwardPath(accountID, domainIdentifier, forwardID))
forwardResponse := &emailForwardResponse{}
resp, err := s.client.delete(path, nil, nil)
if err != nil {
return nil, err
}
forwardResponse.HttpResponse = resp
return forwardResponse, nil
}

View file

@ -0,0 +1,111 @@
package dnsimple
import (
"fmt"
)
// DomainPush represents a domain push in DNSimple.
type DomainPush struct {
ID int `json:"id,omitempty"`
DomainID int `json:"domain_id,omitempty"`
ContactID int `json:"contact_id,omitempty"`
AccountID int `json:"account_id,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
AcceptedAt string `json:"accepted_at,omitempty"`
}
func domainPushPath(accountID string, pushID int) (path string) {
path = fmt.Sprintf("/%v/pushes", accountID)
if pushID != 0 {
path += fmt.Sprintf("/%d", pushID)
}
return
}
// domainPushResponse represents a response from an API method that returns a DomainPush struct.
type domainPushResponse struct {
Response
Data *DomainPush `json:"data"`
}
// domainPushesResponse represents a response from an API method that returns a collection of DomainPush struct.
type domainPushesResponse struct {
Response
Data []DomainPush `json:"data"`
}
// DomainPushAttributes represent a domain push payload (see initiate).
type DomainPushAttributes struct {
NewAccountEmail string `json:"new_account_email,omitempty"`
ContactID string `json:"contact_id,omitempty"`
}
// InitiatePush initiate a new domain push.
//
// See https://developer.dnsimple.com/v2/domains/pushes/#initiate
func (s *DomainsService) InitiatePush(accountID string, domainID string, pushAttributes DomainPushAttributes) (*domainPushResponse, error) {
path := versioned(fmt.Sprintf("/%v/pushes", domainPath(accountID, domainID)))
pushResponse := &domainPushResponse{}
resp, err := s.client.post(path, pushAttributes, pushResponse)
if err != nil {
return nil, err
}
pushResponse.HttpResponse = resp
return pushResponse, nil
}
// ListPushes lists the pushes for an account.
//
// See https://developer.dnsimple.com/v2/domains/pushes/#list
func (s *DomainsService) ListPushes(accountID string, options *ListOptions) (*domainPushesResponse, error) {
path := versioned(domainPushPath(accountID, 0))
pushesResponse := &domainPushesResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, pushesResponse)
if err != nil {
return nil, err
}
pushesResponse.HttpResponse = resp
return pushesResponse, nil
}
// AcceptPush accept a push for a domain.
//
// See https://developer.dnsimple.com/v2/domains/pushes/#accept
func (s *DomainsService) AcceptPush(accountID string, pushID int, pushAttributes DomainPushAttributes) (*domainPushResponse, error) {
path := versioned(domainPushPath(accountID, pushID))
pushResponse := &domainPushResponse{}
resp, err := s.client.post(path, pushAttributes, nil)
if err != nil {
return nil, err
}
pushResponse.HttpResponse = resp
return pushResponse, nil
}
// RejectPush reject a push for a domain.
//
// See https://developer.dnsimple.com/v2/domains/pushes/#reject
func (s *DomainsService) RejectPush(accountID string, pushID int) (*domainPushResponse, error) {
path := versioned(domainPushPath(accountID, pushID))
pushResponse := &domainPushResponse{}
resp, err := s.client.delete(path, nil, nil)
if err != nil {
return nil, err
}
pushResponse.HttpResponse = resp
return pushResponse, nil
}

View file

@ -0,0 +1,48 @@
package dnsimple
// IdentityService handles communication with several authentication identity
// methods of the DNSimple API.
//
// See https://developer.dnsimple.com/v2/identity/
type IdentityService struct {
client *Client
}
// WhoamiData represents an authenticated context
// that contains information about the current logged User and/or Account.
type WhoamiData struct {
User *User `json:"user,omitempty"`
Account *Account `json:"account,omitempty"`
}
// whoamiResponse represents a response from an API method that returns a Whoami struct.
type whoamiResponse struct {
Response
Data *WhoamiData `json:"data"`
}
// Whoami gets the current authenticate context.
//
// See https://developer.dnsimple.com/v2/whoami
func (s *IdentityService) Whoami() (*whoamiResponse, error) {
path := versioned("/whoami")
whoamiResponse := &whoamiResponse{}
resp, err := s.client.get(path, whoamiResponse)
if err != nil {
return nil, err
}
whoamiResponse.HttpResponse = resp
return whoamiResponse, nil
}
// Whoami is a state-less shortcut to client.Whoami()
// that returns only the relevant Data.
func Whoami(c *Client) (data *WhoamiData, err error) {
resp, err := c.Identity.Whoami()
if resp != nil {
data = resp.Data
}
return
}

View file

@ -0,0 +1,113 @@
package dnsimple
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
)
// GrantType is a string that identifies a particular grant type in the exchange request.
type GrantType string
const (
// AuthorizationCodeGrant is the type of access token request
// for an Authorization Code Grant flow.
// https://tools.ietf.org/html/rfc6749#section-4.1
AuthorizationCodeGrant = GrantType("authorization_code")
)
// OauthService handles communication with the authorization related
// methods of the DNSimple API.
//
// See https://developer.dnsimple.com/v2/oauth/
type OauthService struct {
client *Client
}
// AccessToken represents a DNSimple Oauth access token.
type AccessToken struct {
Token string `json:"access_token"`
Type string `json:"token_type"`
AccountID int `json:"account_id"`
}
// ExchangeAuthorizationRequest represents a request to exchange
// an authorization code for an access token.
// RedirectURI is optional, all the other fields are mandatory.
type ExchangeAuthorizationRequest struct {
Code string `json:"code"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
RedirectURI string `json:"redirect_uri,omitempty"`
State string `json:"state,omitempty"`
GrantType GrantType `json:"grant_type,omitempty"`
}
// ExchangeAuthorizationError represents a failed request to exchange
// an authorization code for an access token.
type ExchangeAuthorizationError struct {
// HTTP response
HttpResponse *http.Response
ErrorCode string `json:"error"`
ErrorDescription string `json:"error_description"`
}
// Error implements the error interface.
func (r *ExchangeAuthorizationError) Error() string {
return fmt.Sprintf("%v %v: %v %v",
r.HttpResponse.Request.Method, r.HttpResponse.Request.URL,
r.ErrorCode, r.ErrorDescription)
}
// ExchangeAuthorizationForToken exchanges the short-lived authorization code for an access token
// you can use to authenticate your API calls.
func (s *OauthService) ExchangeAuthorizationForToken(authorization *ExchangeAuthorizationRequest) (*AccessToken, error) {
path := versioned("/oauth/access_token")
req, err := s.client.NewRequest("POST", path, authorization)
if err != nil {
return nil, err
}
resp, err := s.client.HttpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
errorResponse := &ExchangeAuthorizationError{}
errorResponse.HttpResponse = resp
json.NewDecoder(resp.Body).Decode(errorResponse)
return nil, errorResponse
}
accessToken := &AccessToken{}
err = json.NewDecoder(resp.Body).Decode(accessToken)
return accessToken, err
}
// AuthorizationOptions represents the option you can use to generate an authorization URL.
type AuthorizationOptions struct {
RedirectURI string `url:"redirect_uri,omitempty"`
// A randomly generated string to verify the validity of the request.
// Currently "state" is required by the DNSimple OAuth implementation, so you must specify it.
State string `url:"state,omitempty"`
}
// AuthorizeURL generates the URL to authorize an user for an application via the OAuth2 flow.
func (s *OauthService) AuthorizeURL(clientID string, options *AuthorizationOptions) string {
uri, _ := url.Parse(strings.Replace(s.client.BaseURL, "api.", "", 1))
uri.Path = "/oauth/authorize"
query := uri.Query()
query.Add("client_id", clientID)
query.Add("response_type", "code")
uri.RawQuery = query.Encode()
path, _ := addURLQueryOptions(uri.String(), options)
return path
}

View file

@ -0,0 +1,258 @@
package dnsimple
import (
"fmt"
)
// RegistrarService handles communication with the registrar related
// methods of the DNSimple API.
//
// See https://developer.dnsimple.com/v2/registrar/
type RegistrarService struct {
client *Client
}
// DomainCheck represents the result of a domain check.
type DomainCheck struct {
Domain string `json:"domain"`
Available bool `json:"available"`
Premium bool `json:"premium"`
}
// domainCheckResponse represents a response from a domain check request.
type domainCheckResponse struct {
Response
Data *DomainCheck `json:"data"`
}
// CheckDomain checks a domain name.
//
// See https://developer.dnsimple.com/v2/registrar/#check
func (s *RegistrarService) CheckDomain(accountID, domainName string) (*domainCheckResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/check", accountID, domainName))
checkResponse := &domainCheckResponse{}
resp, err := s.client.get(path, checkResponse)
if err != nil {
return nil, err
}
checkResponse.HttpResponse = resp
return checkResponse, nil
}
// DomainPremiumPrice represents the premium price for a premium domain.
type DomainPremiumPrice struct {
// The domain premium price
PremiumPrice string `json:"premium_price"`
// The registrar action.
// Possible values are registration|transfer|renewal
Action string `json:"action"`
}
// domainPremiumPriceResponse represents a response from a domain premium price request.
type domainPremiumPriceResponse struct {
Response
Data *DomainPremiumPrice `json:"data"`
}
// DomainPremiumPriceOptions specifies the optional parameters you can provide
// to customize the RegistrarService.GetDomainPremiumPrice method.
type DomainPremiumPriceOptions struct {
Action string `url:"action,omitempty"`
}
// Gets the premium price for a domain.
//
// You must specify an action to get the price for. Valid actions are:
// - registration
// - transfer
// - renewal
//
// See https://developer.dnsimple.com/v2/registrar/#premium-price
func (s *RegistrarService) GetDomainPremiumPrice(accountID, domainName string, options *DomainPremiumPriceOptions) (*domainPremiumPriceResponse, error) {
var err error
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/premium_price", accountID, domainName))
priceResponse := &domainPremiumPriceResponse{}
if options != nil {
path, err = addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
}
resp, err := s.client.get(path, priceResponse)
if err != nil {
return nil, err
}
priceResponse.HttpResponse = resp
return priceResponse, nil
}
// DomainRegistration represents the result of a domain renewal call.
type DomainRegistration struct {
ID int `json:"id"`
DomainID int `json:"domain_id"`
RegistrantID int `json:"registrant_id"`
Period int `json:"period"`
State string `json:"state"`
AutoRenew bool `json:"auto_renew"`
WhoisPrivacy bool `json:"whois_privacy"`
PremiumPrice string `json:"premium_price"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
// domainRegistrationResponse represents a response from an API method that results in a domain registration.
type domainRegistrationResponse struct {
Response
Data *DomainRegistration `json:"data"`
}
// DomainRegisterRequest represents the attributes you can pass to a register API request.
// Some attributes are mandatory.
type DomainRegisterRequest struct {
// The ID of the Contact to use as registrant for the domain
RegistrantID int `json:"registrant_id"`
// Set to true to enable the whois privacy service. An extra cost may apply.
// Default to false.
EnableWhoisPrivacy bool `json:"whois_privacy,omitempty"`
// Set to true to enable the auto-renewal of the domain.
// Default to true.
EnableAutoRenewal bool `json:"auto_renew,omitempty"`
}
// RegisterDomain registers a domain name.
//
// See https://developer.dnsimple.com/v2/registrar/#register
func (s *RegistrarService) RegisterDomain(accountID string, domainName string, request *DomainRegisterRequest) (*domainRegistrationResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/registrations", accountID, domainName))
registrationResponse := &domainRegistrationResponse{}
// TODO: validate mandatory attributes RegistrantID
resp, err := s.client.post(path, request, registrationResponse)
if err != nil {
return nil, err
}
registrationResponse.HttpResponse = resp
return registrationResponse, nil
}
// DomainTransfer represents the result of a domain renewal call.
type DomainTransfer struct {
ID int `json:"id"`
DomainID int `json:"domain_id"`
RegistrantID int `json:"registrant_id"`
State string `json:"state"`
AutoRenew bool `json:"auto_renew"`
WhoisPrivacy bool `json:"whois_privacy"`
PremiumPrice string `json:"premium_price"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
// domainTransferResponse represents a response from an API method that results in a domain transfer.
type domainTransferResponse struct {
Response
Data *DomainTransfer `json:"data"`
}
// DomainTransferRequest represents the attributes you can pass to a transfer API request.
// Some attributes are mandatory.
type DomainTransferRequest struct {
// The ID of the Contact to use as registrant for the domain
RegistrantID int `json:"registrant_id"`
// The Auth-Code required to transfer the domain.
// This is provided by the current registrar of the domain.
AuthCode string `json:"auth_code,omitempty"`
// Set to true to enable the whois privacy service. An extra cost may apply.
// Default to false.
EnableWhoisPrivacy bool `json:"whois_privacy,omitempty"`
// Set to true to enable the auto-renewal of the domain.
// Default to true.
EnableAutoRenewal bool `json:"auto_renew,omitempty"`
}
// TransferDomain transfers a domain name.
//
// See https://developer.dnsimple.com/v2/registrar/#transfer
func (s *RegistrarService) TransferDomain(accountID string, domainName string, request *DomainTransferRequest) (*domainTransferResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/transfers", accountID, domainName))
transferResponse := &domainTransferResponse{}
// TODO: validate mandatory attributes RegistrantID
resp, err := s.client.post(path, request, transferResponse)
if err != nil {
return nil, err
}
transferResponse.HttpResponse = resp
return transferResponse, nil
}
// domainTransferOutResponse represents a response from an API method that results in a domain transfer out.
type domainTransferOutResponse struct {
Response
Data *Domain `json:"data"`
}
// Transfer out a domain name.
//
// See https://developer.dnsimple.com/v2/registrar/#transfer-out
func (s *RegistrarService) TransferDomainOut(accountID string, domainName string) (*domainTransferOutResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/authorize_transfer_out", accountID, domainName))
transferResponse := &domainTransferOutResponse{}
resp, err := s.client.post(path, nil, nil)
if err != nil {
return nil, err
}
transferResponse.HttpResponse = resp
return transferResponse, nil
}
// DomainRenewal represents the result of a domain renewal call.
type DomainRenewal struct {
ID int `json:"id"`
DomainID int `json:"domain_id"`
Period int `json:"period"`
State string `json:"state"`
PremiumPrice string `json:"premium_price"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
// domainRenewalResponse represents a response from an API method that returns a domain renewal.
type domainRenewalResponse struct {
Response
Data *DomainRenewal `json:"data"`
}
// DomainRenewRequest represents the attributes you can pass to a renew API request.
// Some attributes are mandatory.
type DomainRenewRequest struct {
// The number of years
Period int `json:"period"`
}
// RenewDomain renews a domain name.
//
// See https://developer.dnsimple.com/v2/registrar/#register
func (s *RegistrarService) RenewDomain(accountID string, domainName string, request *DomainRenewRequest) (*domainRenewalResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/renewals", accountID, domainName))
renewalResponse := &domainRenewalResponse{}
resp, err := s.client.post(path, request, renewalResponse)
if err != nil {
return nil, err
}
renewalResponse.HttpResponse = resp
return renewalResponse, nil
}

View file

@ -0,0 +1,37 @@
package dnsimple
import (
"fmt"
)
// EnableDomainAutoRenewal enables auto-renewal for the domain.
//
// See https://developer.dnsimple.com/v2/registrar/auto-renewal/#enable
func (s *RegistrarService) EnableDomainAutoRenewal(accountID string, domainName string) (*domainResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/auto_renewal", accountID, domainName))
domainResponse := &domainResponse{}
resp, err := s.client.put(path, nil, nil)
if err != nil {
return nil, err
}
domainResponse.HttpResponse = resp
return domainResponse, nil
}
// DisableDomainAutoRenewal disables auto-renewal for the domain.
//
// See https://developer.dnsimple.com/v2/registrar/auto-renewal/#enable
func (s *RegistrarService) DisableDomainAutoRenewal(accountID string, domainName string) (*domainResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/auto_renewal", accountID, domainName))
domainResponse := &domainResponse{}
resp, err := s.client.delete(path, nil, nil)
if err != nil {
return nil, err
}
domainResponse.HttpResponse = resp
return domainResponse, nil
}

View file

@ -0,0 +1,84 @@
package dnsimple
import (
"fmt"
)
// Delegation represents a list of name servers that correspond to a domain delegation.
type Delegation []string
// delegationResponse represents a response from an API method that returns a delegation struct.
type delegationResponse struct {
Response
Data *Delegation `json:"data"`
}
// vanityDelegationResponse represents a response for vanity name server enable and disable operations.
type vanityDelegationResponse struct {
Response
Data []VanityNameServer `json:"data"`
}
// GetDomainDelegation gets the current delegated name servers for the domain.
//
// See https://developer.dnsimple.com/v2/registrar/delegation/#get
func (s *RegistrarService) GetDomainDelegation(accountID string, domainName string) (*delegationResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/delegation", accountID, domainName))
delegationResponse := &delegationResponse{}
resp, err := s.client.get(path, delegationResponse)
if err != nil {
return nil, err
}
delegationResponse.HttpResponse = resp
return delegationResponse, nil
}
// ChangeDomainDelegation updates the delegated name severs for the domain.
//
// See https://developer.dnsimple.com/v2/registrar/delegation/#get
func (s *RegistrarService) ChangeDomainDelegation(accountID string, domainName string, newDelegation *Delegation) (*delegationResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/delegation", accountID, domainName))
delegationResponse := &delegationResponse{}
resp, err := s.client.put(path, newDelegation, delegationResponse)
if err != nil {
return nil, err
}
delegationResponse.HttpResponse = resp
return delegationResponse, nil
}
// ChangeDomainDelegationToVanity enables vanity name servers for the given domain.
//
// See https://developer.dnsimple.com/v2/registrar/delegation/#delegateToVanity
func (s *RegistrarService) ChangeDomainDelegationToVanity(accountID string, domainName string, newDelegation *Delegation) (*vanityDelegationResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/delegation/vanity", accountID, domainName))
delegationResponse := &vanityDelegationResponse{}
resp, err := s.client.put(path, newDelegation, delegationResponse)
if err != nil {
return nil, err
}
delegationResponse.HttpResponse = resp
return delegationResponse, nil
}
// ChangeDomainDelegationFromVanity disables vanity name servers for the given domain.
//
// See https://developer.dnsimple.com/v2/registrar/delegation/#dedelegateFromVanity
func (s *RegistrarService) ChangeDomainDelegationFromVanity(accountID string, domainName string) (*vanityDelegationResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/delegation/vanity", accountID, domainName))
delegationResponse := &vanityDelegationResponse{}
resp, err := s.client.delete(path, nil, nil)
if err != nil {
return nil, err
}
delegationResponse.HttpResponse = resp
return delegationResponse, nil
}

View file

@ -0,0 +1,69 @@
package dnsimple
import (
"fmt"
)
// WhoisPrivacy represents a whois privacy in DNSimple.
type WhoisPrivacy struct {
ID int `json:"id,omitempty"`
DomainID int `json:"domain_id,omitempty"`
Enabled bool `json:"enabled,omitempty"`
ExpiresOn string `json:"expires_on,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
// whoisPrivacyResponse represents a response from an API method that returns a WhoisPrivacy struct.
type whoisPrivacyResponse struct {
Response
Data *WhoisPrivacy `json:"data"`
}
// GetWhoisPrivacy gets the whois privacy for the domain.
//
// See https://developer.dnsimple.com/v2/registrar/whois-privacy/#get
func (s *RegistrarService) GetWhoisPrivacy(accountID string, domainName string) (*whoisPrivacyResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/whois_privacy", accountID, domainName))
privacyResponse := &whoisPrivacyResponse{}
resp, err := s.client.get(path, privacyResponse)
if err != nil {
return nil, err
}
privacyResponse.HttpResponse = resp
return privacyResponse, nil
}
// EnableWhoisPrivacy enables the whois privacy for the domain.
//
// See https://developer.dnsimple.com/v2/registrar/whois-privacy/#enable
func (s *RegistrarService) EnableWhoisPrivacy(accountID string, domainName string) (*whoisPrivacyResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/whois_privacy", accountID, domainName))
privacyResponse := &whoisPrivacyResponse{}
resp, err := s.client.put(path, nil, privacyResponse)
if err != nil {
return nil, err
}
privacyResponse.HttpResponse = resp
return privacyResponse, nil
}
// DisablePrivacy disables the whois privacy for the domain.
//
// See https://developer.dnsimple.com/v2/registrar/whois-privacy/#enable
func (s *RegistrarService) DisableWhoisPrivacy(accountID string, domainName string) (*whoisPrivacyResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/whois_privacy", accountID, domainName))
privacyResponse := &whoisPrivacyResponse{}
resp, err := s.client.delete(path, nil, privacyResponse)
if err != nil {
return nil, err
}
privacyResponse.HttpResponse = resp
return privacyResponse, nil
}

View file

@ -0,0 +1,94 @@
package dnsimple
import (
"fmt"
)
// ServicesService handles communication with the service related
// methods of the DNSimple API.
//
// See https://developer.dnsimple.com/v2/services/
type ServicesService struct {
client *Client
}
// Service represents a Service in DNSimple.
type Service struct {
ID int `json:"id,omitempty"`
SID string `json:"sid,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
SetupDescription string `json:"setup_description,omitempty"`
RequiresSetup bool `json:"requires_setup,omitempty"`
DefaultSubdomain string `json:"default_subdomain,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
Settings []ServiceSetting `json:"settings,omitempty"`
}
// ServiceSetting represents a single group of settings for a DNSimple Service.
type ServiceSetting struct {
Name string `json:"name,omitempty"`
Label string `json:"label,omitempty"`
Append string `json:"append,omitempty"`
Description string `json:"description,omitempty"`
Example string `json:"example,omitempty"`
Password bool `json:"password,omitempty"`
}
func servicePath(serviceID string) (path string) {
path = "/services"
if serviceID != "" {
path += fmt.Sprintf("/%v", serviceID)
}
return
}
// serviceResponse represents a response from an API method that returns a Service struct.
type serviceResponse struct {
Response
Data *Service `json:"data"`
}
// servicesResponse represents a response from an API method that returns a collection of Service struct.
type servicesResponse struct {
Response
Data []Service `json:"data"`
}
// ListServices lists the one-click services available in DNSimple.
//
// See https://developer.dnsimple.com/v2/services/#list
func (s *ServicesService) ListServices(options *ListOptions) (*servicesResponse, error) {
path := versioned(servicePath(""))
servicesResponse := &servicesResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, servicesResponse)
if err != nil {
return servicesResponse, err
}
servicesResponse.HttpResponse = resp
return servicesResponse, nil
}
// GetService fetches a one-click service.
//
// See https://developer.dnsimple.com/v2/services/#get
func (s *ServicesService) GetService(serviceIdentifier string) (*serviceResponse, error) {
path := versioned(servicePath(serviceIdentifier))
serviceResponse := &serviceResponse{}
resp, err := s.client.get(path, serviceResponse)
if err != nil {
return nil, err
}
serviceResponse.HttpResponse = resp
return serviceResponse, nil
}

View file

@ -0,0 +1,70 @@
package dnsimple
import (
"fmt"
)
func domainServicesPath(accountID string, domainID string, serviceIdentifier string) string {
if serviceIdentifier != "" {
return fmt.Sprintf("/%v/domains/%v/services/%v", accountID, domainID, serviceIdentifier)
}
return fmt.Sprintf("/%v/domains/%v/services", accountID, domainID)
}
// DomainServiceSettings represents optional settings when applying a DNSimple one-click service to a domain.
type DomainServiceSettings struct {
Settings map[string]string `url:"settings,omitempty"`
}
// AppliedServices lists the applied one-click services for a domain.
//
// See https://developer.dnsimple.com/v2/services/domains/#applied
func (s *ServicesService) AppliedServices(accountID string, domainID string, options *ListOptions) (*servicesResponse, error) {
path := versioned(domainServicesPath(accountID, domainID, ""))
servicesResponse := &servicesResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, servicesResponse)
if err != nil {
return servicesResponse, err
}
servicesResponse.HttpResponse = resp
return servicesResponse, nil
}
// ApplyService applies a one-click services to a domain.
//
// See https://developer.dnsimple.com/v2/services/domains/#apply
func (s *ServicesService) ApplyService(accountID string, serviceIdentifier string, domainID string, settings DomainServiceSettings) (*serviceResponse, error) {
path := versioned(domainServicesPath(accountID, domainID, serviceIdentifier))
serviceResponse := &serviceResponse{}
resp, err := s.client.post(path, settings, nil)
if err != nil {
return nil, err
}
serviceResponse.HttpResponse = resp
return serviceResponse, nil
}
// UnapplyService unapplies a one-click services from a domain.
//
// See https://developer.dnsimple.com/v2/services/domains/#unapply
func (s *ServicesService) UnapplyService(accountID string, serviceIdentifier string, domainID string) (*serviceResponse, error) {
path := versioned(domainServicesPath(accountID, domainID, serviceIdentifier))
serviceResponse := &serviceResponse{}
resp, err := s.client.delete(path, nil, nil)
if err != nil {
return nil, err
}
serviceResponse.HttpResponse = resp
return serviceResponse, nil
}

View file

@ -0,0 +1,129 @@
package dnsimple
import (
"fmt"
)
// TemplatesService handles communication with the template related
// methods of the DNSimple API.
//
// See https://developer.dnsimple.com/v2/templates/
type TemplatesService struct {
client *Client
}
// Template represents a Template in DNSimple.
type Template struct {
ID int `json:"id,omitempty"`
SID string `json:"sid,omitempty"`
AccountID int `json:"account_id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
func templatePath(accountID string, templateIdentifier string) (path string) {
path = fmt.Sprintf("/%v/templates", accountID)
if templateIdentifier != "" {
path += fmt.Sprintf("/%v", templateIdentifier)
}
return
}
// templateResponse represents a response from an API method that returns a Template struct.
type templateResponse struct {
Response
Data *Template `json:"data"`
}
// templatesResponse represents a response from an API method that returns a collection of Template struct.
type templatesResponse struct {
Response
Data []Template `json:"data"`
}
// ListTemplates list the templates for an account.
//
// See https://developer.dnsimple.com/v2/templates/#list
func (s *TemplatesService) ListTemplates(accountID string, options *ListOptions) (*templatesResponse, error) {
path := versioned(templatePath(accountID, ""))
templatesResponse := &templatesResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, templatesResponse)
if err != nil {
return templatesResponse, err
}
templatesResponse.HttpResponse = resp
return templatesResponse, nil
}
// CreateTemplate creates a new template.
//
// See https://developer.dnsimple.com/v2/templates/#create
func (s *TemplatesService) CreateTemplate(accountID string, templateAttributes Template) (*templateResponse, error) {
path := versioned(templatePath(accountID, ""))
templateResponse := &templateResponse{}
resp, err := s.client.post(path, templateAttributes, templateResponse)
if err != nil {
return nil, err
}
templateResponse.HttpResponse = resp
return templateResponse, nil
}
// GetTemplate fetches a template.
//
// See https://developer.dnsimple.com/v2/templates/#get
func (s *TemplatesService) GetTemplate(accountID string, templateIdentifier string) (*templateResponse, error) {
path := versioned(templatePath(accountID, templateIdentifier))
templateResponse := &templateResponse{}
resp, err := s.client.get(path, templateResponse)
if err != nil {
return nil, err
}
templateResponse.HttpResponse = resp
return templateResponse, nil
}
// UpdateTemplate updates a template.
//
// See https://developer.dnsimple.com/v2/templates/#update
func (s *TemplatesService) UpdateTemplate(accountID string, templateIdentifier string, templateAttributes Template) (*templateResponse, error) {
path := versioned(templatePath(accountID, templateIdentifier))
templateResponse := &templateResponse{}
resp, err := s.client.patch(path, templateAttributes, templateResponse)
if err != nil {
return nil, err
}
templateResponse.HttpResponse = resp
return templateResponse, nil
}
// DeleteTemplate deletes a template.
//
// See https://developer.dnsimple.com/v2/templates/#delete
func (s *TemplatesService) DeleteTemplate(accountID string, templateIdentifier string) (*templateResponse, error) {
path := versioned(templatePath(accountID, templateIdentifier))
templateResponse := &templateResponse{}
resp, err := s.client.delete(path, nil, nil)
if err != nil {
return nil, err
}
templateResponse.HttpResponse = resp
return templateResponse, nil
}

View file

@ -0,0 +1,21 @@
package dnsimple
import (
"fmt"
)
// ApplyTemplate applies a template to the given domain.
//
// See https://developer.dnsimple.com/v2/templates/domains/#apply
func (s *TemplatesService) ApplyTemplate(accountID string, templateIdentifier string, domainID string) (*templateResponse, error) {
path := versioned(fmt.Sprintf("%v/templates/%v", domainPath(accountID, domainID), templateIdentifier))
templateResponse := &templateResponse{}
resp, err := s.client.post(path, nil, nil)
if err != nil {
return nil, err
}
templateResponse.HttpResponse = resp
return templateResponse, nil
}

View file

@ -0,0 +1,107 @@
package dnsimple
import (
"fmt"
)
// TemplateRecord represents a DNS record for a template in DNSimple.
type TemplateRecord struct {
ID int `json:"id,omitempty"`
TemplateID int `json:"template_id,omitempty"`
Name string `json:"name"`
Content string `json:"content,omitempty"`
TTL int `json:"ttl,omitempty"`
Type string `json:"type,omitempty"`
Priority int `json:"priority,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
func templateRecordPath(accountID string, templateIdentifier string, templateRecordID string) string {
if templateRecordID != "" {
return fmt.Sprintf("%v/records/%v", templatePath(accountID, templateIdentifier), templateRecordID)
}
return templatePath(accountID, templateIdentifier) + "/records"
}
// templateRecordResponse represents a response from an API method that returns a TemplateRecord struct.
type templateRecordResponse struct {
Response
Data *TemplateRecord `json:"data"`
}
// templateRecordsResponse represents a response from an API method that returns a collection of TemplateRecord struct.
type templateRecordsResponse struct {
Response
Data []TemplateRecord `json:"data"`
}
// ListTemplateRecords list the templates for an account.
//
// See https://developer.dnsimple.com/v2/templates/records/#list
func (s *TemplatesService) ListTemplateRecords(accountID string, templateIdentifier string, options *ListOptions) (*templateRecordsResponse, error) {
path := versioned(templateRecordPath(accountID, templateIdentifier, ""))
templateRecordsResponse := &templateRecordsResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, templateRecordsResponse)
if err != nil {
return templateRecordsResponse, err
}
templateRecordsResponse.HttpResponse = resp
return templateRecordsResponse, nil
}
// CreateTemplateRecord creates a new template record.
//
// See https://developer.dnsimple.com/v2/templates/records/#create
func (s *TemplatesService) CreateTemplateRecord(accountID string, templateIdentifier string, templateRecordAttributes TemplateRecord) (*templateRecordResponse, error) {
path := versioned(templateRecordPath(accountID, templateIdentifier, ""))
templateRecordResponse := &templateRecordResponse{}
resp, err := s.client.post(path, templateRecordAttributes, templateRecordResponse)
if err != nil {
return nil, err
}
templateRecordResponse.HttpResponse = resp
return templateRecordResponse, nil
}
// GetTemplateRecord fetches a template record.
//
// See https://developer.dnsimple.com/v2/templates/records/#get
func (s *TemplatesService) GetTemplateRecord(accountID string, templateIdentifier string, templateRecordID string) (*templateRecordResponse, error) {
path := versioned(templateRecordPath(accountID, templateIdentifier, templateRecordID))
templateRecordResponse := &templateRecordResponse{}
resp, err := s.client.get(path, templateRecordResponse)
if err != nil {
return nil, err
}
templateRecordResponse.HttpResponse = resp
return templateRecordResponse, nil
}
// DeleteTemplateRecord deletes a template record.
//
// See https://developer.dnsimple.com/v2/templates/records/#delete
func (s *TemplatesService) DeleteTemplateRecord(accountID string, templateIdentifier string, templateRecordID string) (*templateRecordResponse, error) {
path := versioned(templateRecordPath(accountID, templateIdentifier, templateRecordID))
templateRecordResponse := &templateRecordResponse{}
resp, err := s.client.delete(path, nil, nil)
if err != nil {
return nil, err
}
templateRecordResponse.HttpResponse = resp
return templateRecordResponse, nil
}

117
vendor/github.com/dnsimple/dnsimple-go/dnsimple/tlds.go generated vendored Normal file
View file

@ -0,0 +1,117 @@
package dnsimple
import (
"fmt"
)
// TldsService handles communication with the Tld related
// methods of the DNSimple API.
//
// See https://developer.dnsimple.com/v2/tlds/
type TldsService struct {
client *Client
}
// Tld represents a TLD in DNSimple.
type Tld struct {
Tld string `json:"tld"`
TldType int `json:"tld_type"`
WhoisPrivacy bool `json:"whois_privacy"`
AutoRenewOnly bool `json:"auto_renew_only"`
MinimumRegistration int `json:"minimum_registration"`
RegistrationEnabled bool `json:"registration_enabled"`
RenewalEnabled bool `json:"renewal_enabled"`
TransferEnabled bool `json:"transfer_enabled"`
}
// TldExtendedAttribute represents an extended attributes supported or required
// by a specific TLD.
//
// See https://developer.dnsimple.com/v2/tlds/
type TldExtendedAttribute struct {
Name string `json:"name"`
Description string `json:"description"`
Required bool `json:"required"`
Options []TldExtendedAttributeOption `json:"options"`
}
// TldExtendedAttributeOption represents a single option you can assign to an extended attributes.
//
// See https://developer.dnsimple.com/v2/tlds/
type TldExtendedAttributeOption struct {
Title string `json:"title"`
Value string `json:"value"`
Description string `json:"description"`
}
// tldResponse represents a response from an API method that returns a Tld struct.
type tldResponse struct {
Response
Data *Tld `json:"data"`
}
// tldsResponse represents a response from an API method that returns a collection of Tld struct.
type tldsResponse struct {
Response
Data []Tld `json:"data"`
}
// tldExtendedAttributesResponse represents a response from an API method that returns
// a collection of Tld extended attributes.
type tldExtendedAttributesResponse struct {
Response
Data []TldExtendedAttribute `json:"data"`
}
// ListTlds lists the supported TLDs.
//
// See https://developer.dnsimple.com/v2/tlds/#list
func (s *TldsService) ListTlds(options *ListOptions) (*tldsResponse, error) {
path := versioned("/tlds")
tldsResponse := &tldsResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, tldsResponse)
if err != nil {
return tldsResponse, err
}
tldsResponse.HttpResponse = resp
return tldsResponse, nil
}
// GetTld fetches a TLD.
//
// See https://developer.dnsimple.com/v2/tlds/#get
func (s *TldsService) GetTld(tld string) (*tldResponse, error) {
path := versioned(fmt.Sprintf("/tlds/%s", tld))
tldResponse := &tldResponse{}
resp, err := s.client.get(path, tldResponse)
if err != nil {
return nil, err
}
tldResponse.HttpResponse = resp
return tldResponse, nil
}
// GetTld fetches the extended attributes of a TLD.
//
// See https://developer.dnsimple.com/v2/tlds/#get
func (s *TldsService) GetTldExtendedAttributes(tld string) (*tldExtendedAttributesResponse, error) {
path := versioned(fmt.Sprintf("/tlds/%s/extended_attributes", tld))
tldResponse := &tldExtendedAttributesResponse{}
resp, err := s.client.get(path, tldResponse)
if err != nil {
return nil, err
}
tldResponse.HttpResponse = resp
return tldResponse, nil
}

View file

@ -0,0 +1,7 @@
package dnsimple
// User represents a DNSimple user.
type User struct {
ID int `json:"id,omitempty"`
Email string `json:"email,omitempty"`
}

View file

@ -0,0 +1,65 @@
package dnsimple
import (
"fmt"
)
// VanityNameServersService handles communication with Vanity Name Servers
// methods of the DNSimple API.
//
// See https://developer.dnsimple.com/v2/vanity/
type VanityNameServersService struct {
client *Client
}
// VanityNameServer represents data for a single vanity name server
type VanityNameServer struct {
ID int `json:"id,omitempty"`
Name string `json:"name,omitempty"`
IPv4 string `json:"ipv4,omitempty"`
IPv6 string `json:"ipv6,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
func vanityNameServerPath(accountID string, domainID string) string {
return fmt.Sprintf("/%v/vanity/%v", accountID, domainID)
}
// vanityNameServerResponse represents a response for vanity name server enable and disable operations.
type vanityNameServerResponse struct {
Response
Data []VanityNameServer `json:"data"`
}
// EnableVanityNameServers Vanity Name Servers for the given domain
//
// See https://developer.dnsimple.com/v2/vanity/#enable
func (s *VanityNameServersService) EnableVanityNameServers(accountID string, domainID string) (*vanityNameServerResponse, error) {
path := versioned(vanityNameServerPath(accountID, domainID))
vanityNameServerResponse := &vanityNameServerResponse{}
resp, err := s.client.put(path, nil, vanityNameServerResponse)
if err != nil {
return nil, err
}
vanityNameServerResponse.HttpResponse = resp
return vanityNameServerResponse, nil
}
// DisableVanityNameServers Vanity Name Servers for the given domain
//
// See https://developer.dnsimple.com/v2/vanity/#disable
func (s *VanityNameServersService) DisableVanityNameServers(accountID string, domainID string) (*vanityNameServerResponse, error) {
path := versioned(vanityNameServerPath(accountID, domainID))
vanityNameServerResponse := &vanityNameServerResponse{}
resp, err := s.client.delete(path, nil, nil)
if err != nil {
return nil, err
}
vanityNameServerResponse.HttpResponse = resp
return vanityNameServerResponse, nil
}

View file

@ -0,0 +1,103 @@
package dnsimple
import (
"fmt"
)
// WebhooksService handles communication with the webhook related
// methods of the DNSimple API.
//
// See https://developer.dnsimple.com/v2/webhooks
type WebhooksService struct {
client *Client
}
// Webhook represents a DNSimple webhook.
type Webhook struct {
ID int `json:"id,omitempty"`
URL string `json:"url,omitempty"`
}
func webhookPath(accountID string, webhookID int) (path string) {
path = fmt.Sprintf("/%v/webhooks", accountID)
if webhookID != 0 {
path = fmt.Sprintf("%v/%v", path, webhookID)
}
return
}
// webhookResponse represents a response from an API method that returns a Webhook struct.
type webhookResponse struct {
Response
Data *Webhook `json:"data"`
}
// webhookResponse represents a response from an API method that returns a collection of Webhook struct.
type webhooksResponse struct {
Response
Data []Webhook `json:"data"`
}
// ListWebhooks lists the webhooks for an account.
//
// See https://developer.dnsimple.com/v2/webhooks#list
func (s *WebhooksService) ListWebhooks(accountID string, _ *ListOptions) (*webhooksResponse, error) {
path := versioned(webhookPath(accountID, 0))
webhooksResponse := &webhooksResponse{}
resp, err := s.client.get(path, webhooksResponse)
if err != nil {
return webhooksResponse, err
}
webhooksResponse.HttpResponse = resp
return webhooksResponse, nil
}
// CreateWebhook creates a new webhook.
//
// See https://developer.dnsimple.com/v2/webhooks#create
func (s *WebhooksService) CreateWebhook(accountID string, webhookAttributes Webhook) (*webhookResponse, error) {
path := versioned(webhookPath(accountID, 0))
webhookResponse := &webhookResponse{}
resp, err := s.client.post(path, webhookAttributes, webhookResponse)
if err != nil {
return nil, err
}
webhookResponse.HttpResponse = resp
return webhookResponse, nil
}
// GetWebhook fetches a webhook.
//
// See https://developer.dnsimple.com/v2/webhooks#get
func (s *WebhooksService) GetWebhook(accountID string, webhookID int) (*webhookResponse, error) {
path := versioned(webhookPath(accountID, webhookID))
webhookResponse := &webhookResponse{}
resp, err := s.client.get(path, webhookResponse)
if err != nil {
return nil, err
}
webhookResponse.HttpResponse = resp
return webhookResponse, nil
}
// DeleteWebhook PERMANENTLY deletes a webhook from the account.
//
// See https://developer.dnsimple.com/v2/webhooks#delete
func (s *WebhooksService) DeleteWebhook(accountID string, webhookID int) (*webhookResponse, error) {
path := versioned(webhookPath(accountID, webhookID))
webhookResponse := &webhookResponse{}
resp, err := s.client.delete(path, nil, nil)
if err != nil {
return nil, err
}
webhookResponse.HttpResponse = resp
return webhookResponse, nil
}

View file

@ -0,0 +1,108 @@
package dnsimple
import (
"fmt"
)
// ZonesService handles communication with the zone related
// methods of the DNSimple API.
//
// See https://developer.dnsimple.com/v2/zones/
type ZonesService struct {
client *Client
}
// Zone represents a Zone in DNSimple.
type Zone struct {
ID int `json:"id,omitempty"`
AccountID int `json:"account_id,omitempty"`
Name string `json:"name,omitempty"`
Reverse bool `json:"reverse,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
// ZoneFile represents a Zone File in DNSimple.
type ZoneFile struct {
Zone string `json:"zone,omitempty"`
}
// zoneResponse represents a response from an API method that returns a Zone struct.
type zoneResponse struct {
Response
Data *Zone `json:"data"`
}
// zonesResponse represents a response from an API method that returns a collection of Zone struct.
type zonesResponse struct {
Response
Data []Zone `json:"data"`
}
// zoneFileResponse represents a response from an API method that returns a ZoneFile struct.
type zoneFileResponse struct {
Response
Data *ZoneFile `json:"data"`
}
// ZoneListOptions specifies the optional parameters you can provide
// to customize the ZonesService.ListZones method.
type ZoneListOptions struct {
// Select domains where the name contains given string.
NameLike string `url:"name_like,omitempty"`
ListOptions
}
// ListZones the zones for an account.
//
// See https://developer.dnsimple.com/v2/zones/#list
func (s *ZonesService) ListZones(accountID string, options *ZoneListOptions) (*zonesResponse, error) {
path := versioned(fmt.Sprintf("/%v/zones", accountID))
zonesResponse := &zonesResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, zonesResponse)
if err != nil {
return zonesResponse, err
}
zonesResponse.HttpResponse = resp
return zonesResponse, nil
}
// GetZone fetches a zone.
//
// See https://developer.dnsimple.com/v2/zones/#get
func (s *ZonesService) GetZone(accountID string, zoneName string) (*zoneResponse, error) {
path := versioned(fmt.Sprintf("/%v/zones/%v", accountID, zoneName))
zoneResponse := &zoneResponse{}
resp, err := s.client.get(path, zoneResponse)
if err != nil {
return nil, err
}
zoneResponse.HttpResponse = resp
return zoneResponse, nil
}
// GetZoneFile fetches a zone file.
//
// See https://developer.dnsimple.com/v2/zones/#get-file
func (s *ZonesService) GetZoneFile(accountID string, zoneName string) (*zoneFileResponse, error) {
path := versioned(fmt.Sprintf("/%v/zones/%v/file", accountID, zoneName))
zoneFileResponse := &zoneFileResponse{}
resp, err := s.client.get(path, zoneFileResponse)
if err != nil {
return nil, err
}
zoneFileResponse.HttpResponse = resp
return zoneFileResponse, nil
}

View file

@ -0,0 +1,142 @@
package dnsimple
import (
"fmt"
)
// ZoneRecord represents a DNS record in DNSimple.
type ZoneRecord struct {
ID int `json:"id,omitempty"`
ZoneID string `json:"zone_id,omitempty"`
ParentID int `json:"parent_id,omitempty"`
Type string `json:"type,omitempty"`
Name string `json:"name"`
Content string `json:"content,omitempty"`
TTL int `json:"ttl,omitempty"`
Priority int `json:"priority,omitempty"`
SystemRecord bool `json:"system_record,omitempty"`
Regions []string `json:"regions,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
func zoneRecordPath(accountID string, zoneID string, recordID int) (path string) {
path = fmt.Sprintf("/%v/zones/%v/records", accountID, zoneID)
if recordID != 0 {
path += fmt.Sprintf("/%d", recordID)
}
return
}
// zoneRecordResponse represents a response from an API method that returns a ZoneRecord struct.
type zoneRecordResponse struct {
Response
Data *ZoneRecord `json:"data"`
}
// zoneRecordsResponse represents a response from an API method that returns a collection of ZoneRecord struct.
type zoneRecordsResponse struct {
Response
Data []ZoneRecord `json:"data"`
}
// ZoneRecordListOptions specifies the optional parameters you can provide
// to customize the ZonesService.ListZoneRecords method.
type ZoneRecordListOptions struct {
// Select records where the name matches given string.
Name string `url:"name,omitempty"`
// Select records where the name contains given string.
NameLike string `url:"name_like,omitempty"`
// Select records of given type.
// Eg. TXT, A, NS.
Type string `url:"record_type,omitempty"`
ListOptions
}
// ListRecords lists the zone records for a zone.
//
// See https://developer.dnsimple.com/v2/zones/#list
func (s *ZonesService) ListRecords(accountID string, zoneID string, options *ZoneRecordListOptions) (*zoneRecordsResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneID, 0))
recordsResponse := &zoneRecordsResponse{}
path, err := addURLQueryOptions(path, options)
if err != nil {
return nil, err
}
resp, err := s.client.get(path, recordsResponse)
if err != nil {
return nil, err
}
recordsResponse.HttpResponse = resp
return recordsResponse, nil
}
// CreateRecord creates a zone record.
//
// See https://developer.dnsimple.com/v2/zones/#create
func (s *ZonesService) CreateRecord(accountID string, zoneID string, recordAttributes ZoneRecord) (*zoneRecordResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneID, 0))
recordResponse := &zoneRecordResponse{}
resp, err := s.client.post(path, recordAttributes, recordResponse)
if err != nil {
return nil, err
}
recordResponse.HttpResponse = resp
return recordResponse, nil
}
// GetRecord fetches a zone record.
//
// See https://developer.dnsimple.com/v2/zones/#get
func (s *ZonesService) GetRecord(accountID string, zoneID string, recordID int) (*zoneRecordResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneID, recordID))
recordResponse := &zoneRecordResponse{}
resp, err := s.client.get(path, recordResponse)
if err != nil {
return nil, err
}
recordResponse.HttpResponse = resp
return recordResponse, nil
}
// UpdateRecord updates a zone record.
//
// See https://developer.dnsimple.com/v2/zones/#update
func (s *ZonesService) UpdateRecord(accountID string, zoneID string, recordID int, recordAttributes ZoneRecord) (*zoneRecordResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneID, recordID))
recordResponse := &zoneRecordResponse{}
resp, err := s.client.patch(path, recordAttributes, recordResponse)
if err != nil {
return nil, err
}
recordResponse.HttpResponse = resp
return recordResponse, nil
}
// DeleteRecord PERMANENTLY deletes a zone record from the zone.
//
// See https://developer.dnsimple.com/v2/zones/#delete
func (s *ZonesService) DeleteRecord(accountID string, zoneID string, recordID int) (*zoneRecordResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneID, recordID))
recordResponse := &zoneRecordResponse{}
resp, err := s.client.delete(path, nil, nil)
if err != nil {
return nil, err
}
recordResponse.HttpResponse = resp
return recordResponse, nil
}

View file

@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2013 Scott Barron
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,73 +0,0 @@
package dnsimple
import (
"encoding/base64"
"fmt"
)
const (
httpHeaderDomainToken = "X-DNSimple-Domain-Token"
httpHeaderApiToken = "X-DNSimple-Token"
httpHeaderAuthorization = "Authorization"
)
// Provides credentials that can be used for authenticating with DNSimple
//
// More information on credentials may be found here:
// http://developer.dnsimple.com/v2/#authentication
type Credentials interface {
// Get the HTTP header key and value to use for authentication.
HttpHeader() (string, string)
}
// Domain token authentication
type domainTokenCredentials struct {
domainToken string
}
// Construct Credentials using the DNSimple Domain Token method
func NewDomainTokenCredentials(domainToken string) Credentials {
return &domainTokenCredentials{domainToken: domainToken}
}
func (c *domainTokenCredentials) HttpHeader() (string, string) {
return httpHeaderDomainToken, c.domainToken
}
// HTTP basic authentication
type httpBasicCredentials struct {
email string
password string
}
// Construct Credentials using HTTP Basic Auth
func NewHttpBasicCredentials(email, password string) Credentials {
return &httpBasicCredentials{email, password}
}
func (c *httpBasicCredentials) HttpHeader() (string, string) {
return httpHeaderAuthorization, "Basic " + basicAuth(c.email, c.password)
}
func basicAuth(username, password string) string {
auth := username + ":" + password
return base64.StdEncoding.EncodeToString([]byte(auth))
}
// API token authentication
type apiTokenCredentials struct {
email string
apiToken string
}
// Construct Credentials using the API Token method.
func NewApiTokenCredentials(email, apiToken string) Credentials {
return &apiTokenCredentials{email: email, apiToken: apiToken}
}
func (c *apiTokenCredentials) HttpHeader() (string, string) {
return httpHeaderApiToken, fmt.Sprintf("%v:%v", c.email, c.apiToken)
}

View file

@ -1,121 +0,0 @@
package dnsimple
import (
"fmt"
"time"
)
// ContactsService handles communication with the contact related
// methods of the DNSimple API.
//
// DNSimple API docs: http://developer.dnsimple.com/contacts/
type ContactsService struct {
client *Client
}
type Contact struct {
Id int `json:"id,omitempty"`
Label string `json:"label,omitempty"`
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
JobTitle string `json:"job_title,omitempty"`
Organization string `json:"organization_name,omitempty"`
Email string `json:"email_address,omitempty"`
Phone string `json:"phone,omitempty"`
Fax string `json:"fax,omitempty"`
Address1 string `json:"address1,omitempty"`
Address2 string `json:"address2,omitempty"`
City string `json:"city,omitempty"`
Zip string `json:"postal_code,omitempty"`
Country string `json:"country,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}
type contactWrapper struct {
Contact Contact `json:"contact"`
}
// contactPath generates the resource path for given contact.
func contactPath(contact interface{}) string {
if contact != nil {
return fmt.Sprintf("contacts/%d", contact)
}
return "contacts"
}
// List the contacts.
//
// DNSimple API docs: http://developer.dnsimple.com/contacts/#list
func (s *ContactsService) List() ([]Contact, *Response, error) {
path := contactPath(nil)
wrappedContacts := []contactWrapper{}
res, err := s.client.get(path, &wrappedContacts)
if err != nil {
return []Contact{}, res, err
}
contacts := []Contact{}
for _, contact := range wrappedContacts {
contacts = append(contacts, contact.Contact)
}
return contacts, res, nil
}
// Create a new contact.
//
// DNSimple API docs: http://developer.dnsimple.com/contacts/#create
func (s *ContactsService) Create(contactAttributes Contact) (Contact, *Response, error) {
path := contactPath(nil)
wrappedContact := contactWrapper{Contact: contactAttributes}
returnedContact := contactWrapper{}
res, err := s.client.post(path, wrappedContact, &returnedContact)
if err != nil {
return Contact{}, res, err
}
return returnedContact.Contact, res, nil
}
// Get fetches a contact.
//
// DNSimple API docs: http://developer.dnsimple.com/contacts/#get
func (s *ContactsService) Get(contactID int) (Contact, *Response, error) {
path := contactPath(contactID)
wrappedContact := contactWrapper{}
res, err := s.client.get(path, &wrappedContact)
if err != nil {
return Contact{}, res, err
}
return wrappedContact.Contact, res, nil
}
// Update a contact.
//
// DNSimple API docs: http://developer.dnsimple.com/contacts/#update
func (s *ContactsService) Update(contactID int, contactAttributes Contact) (Contact, *Response, error) {
path := contactPath(contactID)
wrappedContact := contactWrapper{Contact: contactAttributes}
returnedContact := contactWrapper{}
res, err := s.client.put(path, wrappedContact, &returnedContact)
if err != nil {
return Contact{}, res, err
}
return returnedContact.Contact, res, nil
}
// Delete a contact.
//
// DNSimple API docs: http://developer.dnsimple.com/contacts/#delete
func (s *ContactsService) Delete(contactID int) (*Response, error) {
path := contactPath(contactID)
return s.client.delete(path, nil)
}

View file

@ -1,192 +0,0 @@
// Package dnsimple implements a client for the DNSimple API.
//
// In order to use this package you will need a DNSimple account and your API Token.
package dnsimple
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
const (
libraryVersion = "0.1"
baseURL = "https://api.dnsimple.com/"
userAgent = "dnsimple-go/" + libraryVersion
apiVersion = "v1"
)
type Client struct {
// HTTP client used to communicate with the API.
HttpClient *http.Client
// Credentials used for accessing the DNSimple API
Credentials Credentials
// Base URL for API requests.
// Defaults to the public DNSimple API, but can be set to a different endpoint (e.g. the sandbox).
// BaseURL should always be specified with a trailing slash.
BaseURL string
// User agent used when communicating with the DNSimple API.
UserAgent string
// Services used for talking to different parts of the DNSimple API.
Contacts *ContactsService
Domains *DomainsService
Registrar *RegistrarService
Users *UsersService
}
// NewClient returns a new DNSimple API client.
func NewClient(apiToken, email string) *Client {
return NewAuthenticatedClient(NewApiTokenCredentials(email, apiToken))
}
// NewAuthenticatedClient returns a new DNSimple API client using the given
// credentials.
func NewAuthenticatedClient(credentials Credentials) *Client {
c := &Client{Credentials: credentials, HttpClient: &http.Client{}, BaseURL: baseURL, UserAgent: userAgent}
c.Contacts = &ContactsService{client: c}
c.Domains = &DomainsService{client: c}
c.Registrar = &RegistrarService{client: c}
c.Users = &UsersService{client: c}
return c
}
// NewRequest creates an API request.
// The path is expected to be a relative path and will be resolved
// according to the BaseURL of the Client. Paths should always be specified without a preceding slash.
func (client *Client) NewRequest(method, path string, payload interface{}) (*http.Request, error) {
url := client.BaseURL + fmt.Sprintf("%s/%s", apiVersion, path)
body := new(bytes.Buffer)
if payload != nil {
err := json.NewEncoder(body).Encode(payload)
if err != nil {
return nil, err
}
}
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
req.Header.Add("User-Agent", client.UserAgent)
req.Header.Add(client.Credentials.HttpHeader())
return req, nil
}
func (c *Client) get(path string, v interface{}) (*Response, error) {
return c.Do("GET", path, nil, v)
}
func (c *Client) post(path string, payload, v interface{}) (*Response, error) {
return c.Do("POST", path, payload, v)
}
func (c *Client) put(path string, payload, v interface{}) (*Response, error) {
return c.Do("PUT", path, payload, v)
}
func (c *Client) delete(path string, payload interface{}) (*Response, error) {
return c.Do("DELETE", path, payload, nil)
}
// Do sends an API request and returns the API response.
// The API response is JSON decoded and stored in the value pointed by v,
// or returned as an error if an API error has occurred.
// If v implements the io.Writer interface, the raw response body will be written to v,
// without attempting to decode it.
func (c *Client) Do(method, path string, payload, v interface{}) (*Response, error) {
req, err := c.NewRequest(method, path, payload)
if err != nil {
return nil, err
}
res, err := c.HttpClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
response := &Response{Response: res}
err = CheckResponse(res)
if err != nil {
return response, err
}
if v != nil {
if w, ok := v.(io.Writer); ok {
io.Copy(w, res.Body)
} else {
err = json.NewDecoder(res.Body).Decode(v)
}
}
return response, err
}
// A Response represents an API response.
type Response struct {
*http.Response
}
// An ErrorResponse represents an error caused by an API request.
type ErrorResponse struct {
Response *http.Response // HTTP response that caused this error
Message string `json:"message"` // human-readable message
}
// Error implements the error interface.
func (r *ErrorResponse) Error() string {
return fmt.Sprintf("%v %v: %d %v",
r.Response.Request.Method, r.Response.Request.URL,
r.Response.StatusCode, r.Message)
}
// CheckResponse checks the API response for errors, and returns them if present.
// A response is considered an error if the status code is different than 2xx. Specific requests
// may have additional requirements, but this is sufficient in most of the cases.
func CheckResponse(r *http.Response) error {
if code := r.StatusCode; 200 <= code && code <= 299 {
return nil
}
errorResponse := &ErrorResponse{Response: r}
err := json.NewDecoder(r.Body).Decode(errorResponse)
if err != nil {
return err
}
return errorResponse
}
// Date custom type.
type Date struct {
time.Time
}
// UnmarshalJSON handles the deserialization of the custom Date type.
func (d *Date) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return fmt.Errorf("date should be a string, got %s", data)
}
t, err := time.Parse("2006-01-02", s)
if err != nil {
return fmt.Errorf("invalid date: %v", err)
}
d.Time = t
return nil
}

View file

@ -1,121 +0,0 @@
package dnsimple
import (
"fmt"
"time"
)
// DomainsService handles communication with the domain related
// methods of the DNSimple API.
//
// DNSimple API docs: http://developer.dnsimple.com/domains/
type DomainsService struct {
client *Client
}
type Domain struct {
Id int `json:"id,omitempty"`
UserId int `json:"user_id,omitempty"`
RegistrantId int `json:"registrant_id,omitempty"`
Name string `json:"name,omitempty"`
UnicodeName string `json:"unicode_name,omitempty"`
Token string `json:"token,omitempty"`
State string `json:"state,omitempty"`
Language string `json:"language,omitempty"`
Lockable bool `json:"lockable,omitempty"`
AutoRenew bool `json:"auto_renew,omitempty"`
WhoisProtected bool `json:"whois_protected,omitempty"`
RecordCount int `json:"record_count,omitempty"`
ServiceCount int `json:"service_count,omitempty"`
ExpiresOn *Date `json:"expires_on,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}
type domainWrapper struct {
Domain Domain `json:"domain"`
}
// domainRequest represents a generic wrapper for a domain request,
// when domainWrapper cannot be used because of type constraint on Domain.
type domainRequest struct {
Domain interface{} `json:"domain"`
}
func domainIdentifier(value interface{}) string {
switch value := value.(type) {
case string:
return value
case int:
return fmt.Sprintf("%d", value)
}
return ""
}
// domainPath generates the resource path for given domain.
func domainPath(domain interface{}) string {
if domain != nil {
return fmt.Sprintf("domains/%s", domainIdentifier(domain))
}
return "domains"
}
// List the domains.
//
// DNSimple API docs: http://developer.dnsimple.com/domains/#list
func (s *DomainsService) List() ([]Domain, *Response, error) {
path := domainPath(nil)
returnedDomains := []domainWrapper{}
res, err := s.client.get(path, &returnedDomains)
if err != nil {
return []Domain{}, res, err
}
domains := []Domain{}
for _, domain := range returnedDomains {
domains = append(domains, domain.Domain)
}
return domains, res, nil
}
// Create a new domain.
//
// DNSimple API docs: http://developer.dnsimple.com/domains/#create
func (s *DomainsService) Create(domainAttributes Domain) (Domain, *Response, error) {
path := domainPath(nil)
wrappedDomain := domainWrapper{Domain: domainAttributes}
returnedDomain := domainWrapper{}
res, err := s.client.post(path, wrappedDomain, &returnedDomain)
if err != nil {
return Domain{}, res, err
}
return returnedDomain.Domain, res, nil
}
// Get fetches a domain.
//
// DNSimple API docs: http://developer.dnsimple.com/domains/#get
func (s *DomainsService) Get(domain interface{}) (Domain, *Response, error) {
path := domainPath(domain)
returnedDomain := domainWrapper{}
res, err := s.client.get(path, &returnedDomain)
if err != nil {
return Domain{}, res, err
}
return returnedDomain.Domain, res, nil
}
// Delete a domain.
//
// DNSimple API docs: http://developer.dnsimple.com/domains/#delete
func (s *DomainsService) Delete(domain interface{}) (*Response, error) {
path := domainPath(domain)
return s.client.delete(path, nil)
}

View file

@ -1,136 +0,0 @@
package dnsimple
import (
"fmt"
"net/url"
"time"
)
type Record struct {
Id int `json:"id,omitempty"`
DomainId int `json:"domain_id,omitempty"`
Name string `json:"name,omitempty"`
Content string `json:"content,omitempty"`
TTL int `json:"ttl,omitempty"`
Priority int `json:"prio,omitempty"`
Type string `json:"record_type,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}
type recordWrapper struct {
Record Record `json:"record"`
}
// recordPath generates the resource path for given record that belongs to a domain.
func recordPath(domain interface{}, record interface{}) string {
path := fmt.Sprintf("domains/%s/records", domainIdentifier(domain))
if record != nil {
path += fmt.Sprintf("/%d", record)
}
return path
}
// List the domain records.
//
// DNSimple API docs: http://developer.dnsimple.com/domains/records/#list
func (s *DomainsService) ListRecords(domain interface{}, recordName, recordType string) ([]Record, *Response, error) {
reqStr := recordPath(domain, nil)
v := url.Values{}
if recordName != "" {
v.Add("name", recordName)
}
if recordType != "" {
v.Add("type", recordType)
}
reqStr += "?" + v.Encode()
wrappedRecords := []recordWrapper{}
res, err := s.client.get(reqStr, &wrappedRecords)
if err != nil {
return []Record{}, res, err
}
records := []Record{}
for _, record := range wrappedRecords {
records = append(records, record.Record)
}
return records, res, nil
}
// CreateRecord creates a domain record.
//
// DNSimple API docs: http://developer.dnsimple.com/domains/records/#create
func (s *DomainsService) CreateRecord(domain interface{}, recordAttributes Record) (Record, *Response, error) {
path := recordPath(domain, nil)
wrappedRecord := recordWrapper{Record: recordAttributes}
returnedRecord := recordWrapper{}
res, err := s.client.post(path, wrappedRecord, &returnedRecord)
if err != nil {
return Record{}, res, err
}
return returnedRecord.Record, res, nil
}
// GetRecord fetches the domain record.
//
// DNSimple API docs: http://developer.dnsimple.com/domains/records/#get
func (s *DomainsService) GetRecord(domain interface{}, recordID int) (Record, *Response, error) {
path := recordPath(domain, recordID)
wrappedRecord := recordWrapper{}
res, err := s.client.get(path, &wrappedRecord)
if err != nil {
return Record{}, res, err
}
return wrappedRecord.Record, res, nil
}
// UpdateRecord updates a domain record.
//
// DNSimple API docs: http://developer.dnsimple.com/domains/records/#update
func (s *DomainsService) UpdateRecord(domain interface{}, recordID int, recordAttributes Record) (Record, *Response, error) {
path := recordPath(domain, recordID)
// name, content, ttl, priority
wrappedRecord := recordWrapper{
Record: Record{
Name: recordAttributes.Name,
Content: recordAttributes.Content,
TTL: recordAttributes.TTL,
Priority: recordAttributes.Priority}}
returnedRecord := recordWrapper{}
res, err := s.client.put(path, wrappedRecord, &returnedRecord)
if err != nil {
return Record{}, res, err
}
return returnedRecord.Record, res, nil
}
// DeleteRecord deletes a domain record.
//
// DNSimple API docs: http://developer.dnsimple.com/domains/records/#delete
func (s *DomainsService) DeleteRecord(domain interface{}, recordID int) (*Response, error) {
path := recordPath(domain, recordID)
return s.client.delete(path, nil)
}
// UpdateIP updates the IP of specific A record.
//
// This is not part of the standard API. However,
// this is useful for Dynamic DNS (DDNS or DynDNS).
func (record *Record) UpdateIP(client *Client, IP string) error {
newRecord := Record{Content: IP, Name: record.Name}
_, _, err := client.Domains.UpdateRecord(record.DomainId, record.Id, newRecord)
return err
}

View file

@ -1,24 +0,0 @@
package dnsimple
import (
"fmt"
)
type zoneResponse struct {
Zone string `json:"zone,omitempty"`
}
// GetZone downloads the Bind-like zone file.
//
// DNSimple API docs: http://developer.dnsimple.com/domains/zones/#get
func (s *DomainsService) GetZone(domain interface{}) (string, *Response, error) {
path := fmt.Sprintf("%s/zone", domainPath(domain))
zoneResponse := zoneResponse{}
res, err := s.client.get(path, &zoneResponse)
if err != nil {
return "", res, err
}
return zoneResponse.Zone, res, nil
}

View file

@ -1,131 +0,0 @@
package dnsimple
import (
"fmt"
)
// RegistrarService handles communication with the registrar related
// methods of the DNSimple API.
//
// DNSimple API docs: http://developer.dnsimple.com/registrar/
type RegistrarService struct {
client *Client
}
// ExtendedAttributes maps the additional attributes required by some registries.
type ExtendedAttributes map[string]string
// ExtendedAttributes represents the transfer information.
type TransferOrder struct {
AuthCode string `json:"authinfo,omitempty"`
}
// registrationRequest represents the body of a register or transfer request.
type registrationRequest struct {
Domain Domain `json:"domain"`
ExtendedAttributes *ExtendedAttributes `json:"extended_attribute,omitempty"`
TransferOrder *TransferOrder `json:"transfer_order,omitempty"`
}
// IsAvailable checks if the domain is available or registered.
//
// See: http://developer.dnsimple.com/registrar/#check
func (s *RegistrarService) IsAvailable(domain string) (bool, error) {
path := fmt.Sprintf("%s/check", domainPath(domain))
res, err := s.client.get(path, nil)
if err != nil && res != nil && res.StatusCode != 404 {
return false, err
}
return res.StatusCode == 404, nil
}
// Register a domain.
//
// DNSimple API docs: http://developer.dnsimple.com/registrar/#register
func (s *RegistrarService) Register(domain string, registrantID int, extendedAttributes *ExtendedAttributes) (Domain, *Response, error) {
request := registrationRequest{
Domain: Domain{Name: domain, RegistrantId: registrantID},
ExtendedAttributes: extendedAttributes,
}
returnedDomain := domainWrapper{}
res, err := s.client.post("domain_registrations", request, &returnedDomain)
if err != nil {
return Domain{}, res, err
}
return returnedDomain.Domain, res, nil
}
// Transfer a domain.
//
// DNSimple API docs: http://developer.dnsimple.com/registrar/#transfer
func (s *RegistrarService) Transfer(domain string, registrantID int, authCode string, extendedAttributes *ExtendedAttributes) (Domain, *Response, error) {
request := registrationRequest{
Domain: Domain{Name: domain, RegistrantId: registrantID},
ExtendedAttributes: extendedAttributes,
TransferOrder: &TransferOrder{AuthCode: authCode},
}
returnedDomain := domainWrapper{}
res, err := s.client.post("domain_transfers", request, &returnedDomain)
if err != nil {
return Domain{}, res, err
}
return returnedDomain.Domain, res, nil
}
// renewDomain represents the body of a Renew request.
type renewDomain struct {
Name string `json:"name,omitempty"`
RenewWhoisPrivacy bool `json:"renew_whois_privacy,omitempty"`
}
// Renew the domain, optionally renewing WHOIS privacy service.
//
// DNSimple API docs: http://developer.dnsimple.com/registrar/#renew
func (s *RegistrarService) Renew(domain string, renewWhoisPrivacy bool) (Domain, *Response, error) {
request := domainRequest{Domain: renewDomain{
Name: domain,
RenewWhoisPrivacy: renewWhoisPrivacy,
}}
returnedDomain := domainWrapper{}
res, err := s.client.post("domain_renewals", request, &returnedDomain)
if err != nil {
return Domain{}, res, err
}
return returnedDomain.Domain, res, nil
}
// EnableAutoRenewal enables the auto-renewal feature for the domain.
//
// DNSimple API docs: http://developer.dnsimple.com/registrar/autorenewal/#enable
func (s *RegistrarService) EnableAutoRenewal(domain interface{}) (*Response, error) {
path := fmt.Sprintf("%s/auto_renewal", domainPath(domain))
res, err := s.client.post(path, nil, nil)
if err != nil {
return res, err
}
return res, nil
}
// DisableAutoRenewal disables the auto-renewal feature for the domain.
//
// DNSimple API docs: http://developer.dnsimple.com/registrar/autorenewal/#disable
func (s *RegistrarService) DisableAutoRenewal(domain interface{}) (*Response, error) {
path := fmt.Sprintf("%s/auto_renewal", domainPath(domain))
res, err := s.client.delete(path, nil)
if err != nil {
return res, err
}
return res, nil
}

View file

@ -1,35 +0,0 @@
package dnsimple
import ()
// UsersService handles communication with the uer related
// methods of the DNSimple API.
//
// DNSimple API docs: http://developer.dnsimple.com/users/
type UsersService struct {
client *Client
}
// User represents a DNSimple user.
type User struct {
Id int `json:"id,omitempty"`
Email string `json:"email,omitempty"`
}
type userWrapper struct {
User User `json:"user"`
}
// User gets the logged in user.
//
// DNSimple API docs: http://developer.dnsimple.com/users/
func (s *UsersService) User() (User, *Response, error) {
wrappedUser := userWrapper{}
res, err := s.client.get("user", &wrappedUser)
if err != nil {
return User{}, res, err
}
return wrappedUser.User, res, nil
}

View file

@ -11,6 +11,7 @@ import (
"io/ioutil"
"log"
"net"
"net/http"
"regexp"
"strconv"
"strings"
@ -22,6 +23,16 @@ var (
Logger *log.Logger
)
const (
// maxBodySize is the maximum size of body that we will read.
maxBodySize = 1024 * 1024
// overallRequestLimit is the overall number of request per second limited on the
// “new-reg”, “new-authz” and “new-cert” endpoints. From the documentation the
// limitation is 20 requests per second, but using 20 as value doesn't work but 18 do
overallRequestLimit = 18
)
// logf writes a log entry. It uses Logger if not
// nil, otherwise it uses the default log.Logger.
func logf(format string, args ...interface{}) {
@ -518,7 +529,11 @@ func (c *Client) chooseSolvers(auth authorization, domain string) map[int]solver
func (c *Client) getChallenges(domains []string) ([]authorizationResource, map[string]error) {
resc, errc := make(chan authorizationResource), make(chan domainError)
delay := time.Second / overallRequestLimit
for _, domain := range domains {
time.Sleep(delay)
go func(domain string) {
authMsg := authorization{Resource: "new-authz", Identifier: identifier{Type: "dns", Value: domain}}
var authz authorization
@ -531,6 +546,7 @@ func (c *Client) getChallenges(domains []string) ([]authorizationResource, map[s
links := parseLinks(hdr["Link"])
if links["next"] == "" {
logf("[ERROR][%s] acme: Server did not provide next link to proceed", domain)
errc <- domainError{Domain: domain, Error: errors.New("Server did not provide next link to proceed")}
return
}
@ -556,12 +572,20 @@ func (c *Client) getChallenges(domains []string) ([]authorizationResource, map[s
}
}
logAuthz(challenges)
close(resc)
close(errc)
return challenges, failures
}
func logAuthz(authz []authorizationResource) {
for _, auth := range authz {
logf("[INFO][%s] AuthURL: %s", auth.Domain, auth.AuthURL)
}
}
func (c *Client) requestCertificate(authz []authorizationResource, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, error) {
if len(authz) == 0 {
return CertificateResource{}, errors.New("Passed no authorizations to requestCertificate!")
@ -610,73 +634,95 @@ func (c *Client) requestCertificateForCsr(authz []authorizationResource, bundle
return CertificateResource{}, err
}
cerRes := CertificateResource{
certRes := CertificateResource{
Domain: commonName.Domain,
CertURL: resp.Header.Get("Location"),
PrivateKey: privateKeyPem}
PrivateKey: privateKeyPem,
}
for {
switch resp.StatusCode {
case 201, 202:
cert, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
resp.Body.Close()
if err != nil {
return CertificateResource{}, err
}
// The server returns a body with a length of zero if the
// certificate was not ready at the time this request completed.
// Otherwise the body is the certificate.
if len(cert) > 0 {
cerRes.CertStableURL = resp.Header.Get("Content-Location")
cerRes.AccountRef = c.user.GetRegistration().URI
issuedCert := pemEncode(derCertificateBytes(cert))
// The issuer certificate link is always supplied via an "up" link
// in the response headers of a new certificate.
links := parseLinks(resp.Header["Link"])
issuerCert, err := c.getIssuerCertificate(links["up"])
if err != nil {
// If we fail to acquire the issuer cert, return the issued certificate - do not fail.
logf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", commonName.Domain, err)
} else {
issuerCert = pemEncode(derCertificateBytes(issuerCert))
// If bundle is true, we want to return a certificate bundle.
// To do this, we append the issuer cert to the issued cert.
if bundle {
issuedCert = append(issuedCert, issuerCert...)
}
}
cerRes.Certificate = issuedCert
cerRes.IssuerCertificate = issuerCert
logf("[INFO][%s] Server responded with a certificate.", commonName.Domain)
return cerRes, nil
}
// The certificate was granted but is not yet issued.
// Check retry-after and loop.
ra := resp.Header.Get("Retry-After")
retryAfter, err := strconv.Atoi(ra)
if err != nil {
return CertificateResource{}, err
}
logf("[INFO][%s] acme: Server responded with status 202; retrying after %ds", commonName.Domain, retryAfter)
time.Sleep(time.Duration(retryAfter) * time.Second)
break
default:
return CertificateResource{}, handleHTTPError(resp)
}
resp, err = httpGet(cerRes.CertURL)
maxChecks := 1000
for i := 0; i < maxChecks; i++ {
done, err := c.checkCertResponse(resp, &certRes, bundle)
resp.Body.Close()
if err != nil {
return CertificateResource{}, err
}
if done {
break
}
if i == maxChecks-1 {
return CertificateResource{}, fmt.Errorf("polled for certificate %d times; giving up", i)
}
resp, err = httpGet(certRes.CertURL)
if err != nil {
return CertificateResource{}, err
}
}
return certRes, nil
}
// checkCertResponse checks resp to see if a certificate is contained in the
// response, and if so, loads it into certRes and returns true. If the cert
// is not yet ready, it returns false. This function honors the waiting period
// required by the Retry-After header of the response, if specified. This
// function may read from resp.Body but does NOT close it. The certRes input
// should already have the Domain (common name) field populated. If bundle is
// true, the certificate will be bundled with the issuer's cert.
func (c *Client) checkCertResponse(resp *http.Response, certRes *CertificateResource, bundle bool) (bool, error) {
switch resp.StatusCode {
case 201, 202:
cert, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize))
if err != nil {
return false, err
}
// The server returns a body with a length of zero if the
// certificate was not ready at the time this request completed.
// Otherwise the body is the certificate.
if len(cert) > 0 {
certRes.CertStableURL = resp.Header.Get("Content-Location")
certRes.AccountRef = c.user.GetRegistration().URI
issuedCert := pemEncode(derCertificateBytes(cert))
// The issuer certificate link is always supplied via an "up" link
// in the response headers of a new certificate.
links := parseLinks(resp.Header["Link"])
issuerCert, err := c.getIssuerCertificate(links["up"])
if err != nil {
// If we fail to acquire the issuer cert, return the issued certificate - do not fail.
logf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err)
} else {
issuerCert = pemEncode(derCertificateBytes(issuerCert))
// If bundle is true, we want to return a certificate bundle.
// To do this, we append the issuer cert to the issued cert.
if bundle {
issuedCert = append(issuedCert, issuerCert...)
}
}
certRes.Certificate = issuedCert
certRes.IssuerCertificate = issuerCert
logf("[INFO][%s] Server responded with a certificate.", certRes.Domain)
return true, nil
}
// The certificate was granted but is not yet issued.
// Check retry-after and loop.
ra := resp.Header.Get("Retry-After")
retryAfter, err := strconv.Atoi(ra)
if err != nil {
return false, err
}
logf("[INFO][%s] acme: Server responded with status 202; retrying after %ds", certRes.Domain, retryAfter)
time.Sleep(time.Duration(retryAfter) * time.Second)
return false, nil
default:
return false, handleHTTPError(resp)
}
}
@ -689,7 +735,7 @@ func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
}
defer resp.Body.Close()
issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize))
if err != nil {
return nil, err
}

View file

@ -10,6 +10,7 @@ import (
const (
tosAgreementError = "Must agree to subscriber agreement before any further actions"
invalidNonceError = "JWS has invalid anti-replay nonce"
)
// RemoteError is the base type for all errors specific to the ACME protocol.
@ -30,6 +31,12 @@ type TOSError struct {
RemoteError
}
// NonceError represents the error which is returned if the
// nonce sent by the client was not accepted by the server.
type NonceError struct {
RemoteError
}
type domainError struct {
Domain string
Error error
@ -54,20 +61,17 @@ func (c challengeError) Error() string {
func handleHTTPError(resp *http.Response) error {
var errorDetail RemoteError
contenType := resp.Header.Get("Content-Type")
// try to decode the content as JSON
if contenType == "application/json" || contenType == "application/problem+json" {
decoder := json.NewDecoder(resp.Body)
err := decoder.Decode(&errorDetail)
contentType := resp.Header.Get("Content-Type")
if contentType == "application/json" || contentType == "application/problem+json" {
err := json.NewDecoder(resp.Body).Decode(&errorDetail)
if err != nil {
return err
}
} else {
detailBytes, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
detailBytes, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize))
if err != nil {
return err
}
errorDetail.Detail = string(detailBytes)
}
@ -78,6 +82,10 @@ func handleHTTPError(resp *http.Response) error {
return TOSError{errorDetail}
}
if errorDetail.StatusCode == http.StatusBadRequest && strings.HasPrefix(errorDetail.Detail, invalidNonceError) {
return NonceError{errorDetail}
}
return errorDetail
}

View file

@ -31,14 +31,14 @@ const (
func httpHead(url string) (resp *http.Response, err error) {
req, err := http.NewRequest("HEAD", url, nil)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to head %q: %v", url, err)
}
req.Header.Set("User-Agent", userAgent())
resp, err = HTTPClient.Do(req)
if err != nil {
return resp, err
return resp, fmt.Errorf("failed to do head %q: %v", url, err)
}
resp.Body.Close()
return resp, err
@ -49,7 +49,7 @@ func httpHead(url string) (resp *http.Response, err error) {
func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response, err error) {
req, err := http.NewRequest("POST", url, body)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to post %q: %v", url, err)
}
req.Header.Set("Content-Type", bodyType)
req.Header.Set("User-Agent", userAgent())
@ -62,7 +62,7 @@ func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response,
func httpGet(url string) (resp *http.Response, err error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get %q: %v", url, err)
}
req.Header.Set("User-Agent", userAgent())
@ -74,7 +74,7 @@ func httpGet(url string) (resp *http.Response, err error) {
func getJSON(uri string, respBody interface{}) (http.Header, error) {
resp, err := httpGet(uri)
if err != nil {
return nil, fmt.Errorf("failed to get %q: %v", uri, err)
return nil, fmt.Errorf("failed to get json %q: %v", uri, err)
}
defer resp.Body.Close()
@ -97,10 +97,41 @@ func postJSON(j *jws, uri string, reqBody, respBody interface{}) (http.Header, e
if err != nil {
return nil, fmt.Errorf("Failed to post JWS message. -> %v", err)
}
defer resp.Body.Close()
if resp.StatusCode >= http.StatusBadRequest {
return resp.Header, handleHTTPError(resp)
err := handleHTTPError(resp)
switch err.(type) {
case NonceError:
// Retry once if the nonce was invalidated
retryResp, err := j.post(uri, jsonBytes)
if err != nil {
return nil, fmt.Errorf("Failed to post JWS message. -> %v", err)
}
defer retryResp.Body.Close()
if retryResp.StatusCode >= http.StatusBadRequest {
return retryResp.Header, handleHTTPError(retryResp)
}
if respBody == nil {
return retryResp.Header, nil
}
return retryResp.Header, json.NewDecoder(retryResp.Body).Decode(respBody)
default:
return resp.Header, err
}
}
if respBody == nil {

View file

@ -16,8 +16,7 @@ import (
type jws struct {
directoryURL string
privKey crypto.PrivateKey
nonces []string
sync.Mutex
nonces nonceManager
}
func keyAsJWK(key interface{}) *jose.JsonWebKey {
@ -32,23 +31,26 @@ func keyAsJWK(key interface{}) *jose.JsonWebKey {
}
}
// Posts a JWS signed message to the specified URL
// Posts a JWS signed message to the specified URL.
// It does NOT close the response body, so the caller must
// do that if no error was returned.
func (j *jws) post(url string, content []byte) (*http.Response, error) {
signedContent, err := j.signContent(content)
if err != nil {
return nil, err
return nil, fmt.Errorf("Failed to sign content -> %s", err.Error())
}
resp, err := httpPost(url, "application/jose+json", bytes.NewBuffer([]byte(signedContent.FullSerialize())))
if err != nil {
return nil, err
return nil, fmt.Errorf("Failed to HTTP POST to %s -> %s", url, err.Error())
}
j.Lock()
defer j.Unlock()
j.getNonceFromResponse(resp)
nonce, nonceErr := getNonceFromResponse(resp)
if nonceErr == nil {
j.nonces.Push(nonce)
}
return resp, err
return resp, nil
}
func (j *jws) signContent(content []byte) (*jose.JsonWebSignature, error) {
@ -67,49 +69,63 @@ func (j *jws) signContent(content []byte) (*jose.JsonWebSignature, error) {
signer, err := jose.NewSigner(alg, j.privKey)
if err != nil {
return nil, err
return nil, fmt.Errorf("Failed to create jose signer -> %s", err.Error())
}
signer.SetNonceSource(j)
signed, err := signer.Sign(content)
if err != nil {
return nil, err
return nil, fmt.Errorf("Failed to sign content -> %s", err.Error())
}
return signed, nil
}
func (j *jws) getNonceFromResponse(resp *http.Response) error {
func (j *jws) Nonce() (string, error) {
if nonce, ok := j.nonces.Pop(); ok {
return nonce, nil
}
return getNonce(j.directoryURL)
}
type nonceManager struct {
nonces []string
sync.Mutex
}
func (n *nonceManager) Pop() (string, bool) {
n.Lock()
defer n.Unlock()
if len(n.nonces) == 0 {
return "", false
}
nonce := n.nonces[len(n.nonces)-1]
n.nonces = n.nonces[:len(n.nonces)-1]
return nonce, true
}
func (n *nonceManager) Push(nonce string) {
n.Lock()
defer n.Unlock()
n.nonces = append(n.nonces, nonce)
}
func getNonce(url string) (string, error) {
resp, err := httpHead(url)
if err != nil {
return "", fmt.Errorf("Failed to get nonce from HTTP HEAD -> %s", err.Error())
}
return getNonceFromResponse(resp)
}
func getNonceFromResponse(resp *http.Response) (string, error) {
nonce := resp.Header.Get("Replay-Nonce")
if nonce == "" {
return fmt.Errorf("Server did not respond with a proper nonce header.")
return "", fmt.Errorf("Server did not respond with a proper nonce header.")
}
j.nonces = append(j.nonces, nonce)
return nil
}
func (j *jws) getNonce() error {
resp, err := httpHead(j.directoryURL)
if err != nil {
return err
}
return j.getNonceFromResponse(resp)
}
func (j *jws) Nonce() (string, error) {
j.Lock()
defer j.Unlock()
nonce := ""
if len(j.nonces) == 0 {
err := j.getNonce()
if err != nil {
return nonce, err
}
}
if len(j.nonces) == 0 {
return "", fmt.Errorf("Can't get nonce")
}
nonce, j.nonces = j.nonces[len(j.nonces)-1], j.nonces[:len(j.nonces)-1]
return nonce, nil
}

View file

@ -205,7 +205,7 @@ Here is an example bash command using the CloudFlare DNS provider:
fmt.Fprintln(w, "\tauroradns:\tAURORA_USER_ID, AURORA_KEY, AURORA_ENDPOINT")
fmt.Fprintln(w, "\tcloudflare:\tCLOUDFLARE_EMAIL, CLOUDFLARE_API_KEY")
fmt.Fprintln(w, "\tdigitalocean:\tDO_AUTH_TOKEN")
fmt.Fprintln(w, "\tdnsimple:\tDNSIMPLE_EMAIL, DNSIMPLE_API_KEY")
fmt.Fprintln(w, "\tdnsimple:\tDNSIMPLE_EMAIL, DNSIMPLE_OAUTH_TOKEN")
fmt.Fprintln(w, "\tdnsmadeeasy:\tDNSMADEEASY_API_KEY, DNSMADEEASY_API_SECRET")
fmt.Fprintln(w, "\texoscale:\tEXOSCALE_API_KEY, EXOSCALE_API_SECRET, EXOSCALE_ENDPOINT")
fmt.Fprintln(w, "\tgandi:\tGANDI_API_KEY")

View file

@ -10,10 +10,11 @@ import (
"github.com/Azure/azure-sdk-for-go/arm/dns"
"strings"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/to"
"github.com/xenolf/lego/acme"
"strings"
)
// DNSProvider is an implementation of the acme.ChallengeProvider interface
@ -74,7 +75,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
Name: &relative,
RecordSetProperties: &dns.RecordSetProperties{
TTL: to.Int64Ptr(60),
TXTRecords: &[]dns.TxtRecord{dns.TxtRecord{Value: &[]string{value}}},
TxtRecords: &[]dns.TxtRecord{dns.TxtRecord{Value: &[]string{value}}},
},
}
_, err = rsc.CreateOrUpdate(c.resourceGroup, zone, relative, dns.TXT, rec, "", "")

View file

@ -5,9 +5,10 @@ package dnsimple
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/weppos/dnsimple-go/dnsimple"
"github.com/dnsimple/dnsimple-go/dnsimple"
"github.com/xenolf/lego/acme"
)
@ -17,37 +18,50 @@ type DNSProvider struct {
}
// NewDNSProvider returns a DNSProvider instance configured for dnsimple.
// Credentials must be passed in the environment variables: DNSIMPLE_EMAIL
// and DNSIMPLE_API_KEY.
// Credentials must be passed in the environment variables: DNSIMPLE_OAUTH_TOKEN.
//
// See: https://developer.dnsimple.com/v2/#authentication
func NewDNSProvider() (*DNSProvider, error) {
email := os.Getenv("DNSIMPLE_EMAIL")
key := os.Getenv("DNSIMPLE_API_KEY")
return NewDNSProviderCredentials(email, key)
accessToken := os.Getenv("DNSIMPLE_OAUTH_TOKEN")
baseUrl := os.Getenv("DNSIMPLE_BASE_URL")
return NewDNSProviderCredentials(accessToken, baseUrl)
}
// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for dnsimple.
func NewDNSProviderCredentials(email, key string) (*DNSProvider, error) {
if email == "" || key == "" {
return nil, fmt.Errorf("DNSimple credentials missing")
func NewDNSProviderCredentials(accessToken, baseUrl string) (*DNSProvider, error) {
if accessToken == "" {
return nil, fmt.Errorf("DNSimple OAuth token is missing")
}
return &DNSProvider{
client: dnsimple.NewClient(key, email),
}, nil
client := dnsimple.NewClient(dnsimple.NewOauthTokenCredentials(accessToken))
client.UserAgent = "lego"
if baseUrl != "" {
client.BaseURL = baseUrl
}
return &DNSProvider{client: client}, nil
}
// Present creates a TXT record to fulfil the dns-01 challenge.
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
zoneID, zoneName, err := c.getHostedZone(domain)
zoneName, err := c.getHostedZone(domain)
if err != nil {
return err
}
accountID, err := c.getAccountID()
if err != nil {
return err
}
recordAttributes := c.newTxtRecord(zoneName, fqdn, value, ttl)
_, _, err = c.client.Domains.CreateRecord(zoneID, *recordAttributes)
_, err = c.client.Zones.CreateRecord(accountID, zoneName, *recordAttributes)
if err != nil {
return fmt.Errorf("DNSimple API call failed: %v", err)
}
@ -64,67 +78,79 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
return err
}
accountID, err := c.getAccountID()
if err != nil {
return err
}
for _, rec := range records {
_, err := c.client.Domains.DeleteRecord(rec.DomainId, rec.Id)
_, err := c.client.Zones.DeleteRecord(accountID, rec.ZoneID, rec.ID)
if err != nil {
return err
}
}
return nil
}
func (c *DNSProvider) getHostedZone(domain string) (string, string, error) {
zones, _, err := c.client.Domains.List()
if err != nil {
return "", "", fmt.Errorf("DNSimple API call failed: %v", err)
}
func (c *DNSProvider) getHostedZone(domain string) (string, error) {
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
if err != nil {
return "", "", err
return "", err
}
var hostedZone dnsimple.Domain
for _, zone := range zones {
if zone.Name == acme.UnFqdn(authZone) {
accountID, err := c.getAccountID()
if err != nil {
return "", err
}
zoneName := acme.UnFqdn(authZone)
zones, err := c.client.Zones.ListZones(accountID, &dnsimple.ZoneListOptions{NameLike: zoneName})
if err != nil {
return "", fmt.Errorf("DNSimple API call failed: %v", err)
}
var hostedZone dnsimple.Zone
for _, zone := range zones.Data {
if zone.Name == zoneName {
hostedZone = zone
}
}
if hostedZone.Id == 0 {
return "", "", fmt.Errorf("Zone %s not found in DNSimple for domain %s", authZone, domain)
if hostedZone.ID == 0 {
return "", fmt.Errorf("Zone %s not found in DNSimple for domain %s", authZone, domain)
}
return fmt.Sprintf("%v", hostedZone.Id), hostedZone.Name, nil
return hostedZone.Name, nil
}
func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.Record, error) {
zoneID, zoneName, err := c.getHostedZone(domain)
func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.ZoneRecord, error) {
zoneName, err := c.getHostedZone(domain)
if err != nil {
return nil, err
}
var records []dnsimple.Record
result, _, err := c.client.Domains.ListRecords(zoneID, "", "TXT")
accountID, err := c.getAccountID()
if err != nil {
return records, fmt.Errorf("DNSimple API call has failed: %v", err)
return nil, err
}
recordName := c.extractRecordName(fqdn, zoneName)
for _, record := range result {
if record.Name == recordName {
records = append(records, record)
}
result, err := c.client.Zones.ListRecords(accountID, zoneName, &dnsimple.ZoneRecordListOptions{Name: recordName, Type: "TXT", ListOptions: dnsimple.ListOptions{}})
if err != nil {
return []dnsimple.ZoneRecord{}, fmt.Errorf("DNSimple API call has failed: %v", err)
}
return records, nil
return result.Data, nil
}
func (c *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnsimple.Record {
name := c.extractRecordName(fqdn, zone)
func (c *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsimple.ZoneRecord {
name := c.extractRecordName(fqdn, zoneName)
return &dnsimple.Record{
return &dnsimple.ZoneRecord{
Type: "TXT",
Name: name,
Content: value,
@ -139,3 +165,16 @@ func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
}
return name
}
func (c *DNSProvider) getAccountID() (string, error) {
whoamiResponse, err := c.client.Identity.Whoami()
if err != nil {
return "", err
}
if whoamiResponse.Data.Account == nil {
return "", fmt.Errorf("DNSimple user tokens are not supported, please use an account token.")
}
return strconv.Itoa(whoamiResponse.Data.Account.ID), nil
}