@ -7,17 +7,17 @@ import (
"io"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/hashicorp/go-cleanhttp"
)
type Client struct {
// Location of PowerDNS server to use
ServerUrl string
// REST API Static authentication key
ApiKey string
Http * http . Client
ServerUrl string // Location of PowerDNS server to use
ApiKey string // REST API Static authentication key
ApiVersion int // API version to use
Http * http . Client
}
// NewClient returns a new PowerDNS client
@ -27,15 +27,26 @@ func NewClient(serverUrl string, apiKey string) (*Client, error) {
ApiKey : apiKey ,
Http : cleanhttp . DefaultClient ( ) ,
}
var err error
client . ApiVersion , err = client . detectApiVersion ( )
if err != nil {
return nil , err
}
return & client , nil
}
// Creates a new request with necessary headers
func ( c * Client ) newRequest ( method string , endpoint string , body [ ] byte ) ( * http . Request , error ) {
url , err := url . Parse ( c . ServerUrl + endpoint )
var urlStr string
if c . ApiVersion > 0 {
urlStr = c . ServerUrl + "/api/v" + strconv . Itoa ( c . ApiVersion ) + endpoint
} else {
urlStr = c . ServerUrl + endpoint
}
url , err := url . Parse ( urlStr )
if err != nil {
return nil , fmt . Errorf ( "Error during parting request URL: %s" , err )
return nil , fmt . Errorf ( "Error during par s ing request URL: %s", err )
}
var bodyReader io . Reader
@ -59,20 +70,21 @@ func (c *Client) newRequest(method string, endpoint string, body []byte) (*http.
}
type ZoneInfo struct {
Id string ` json:"id" `
Name string ` json:"name" `
URL string ` json:"url" `
Kind string ` json:"kind" `
DnsSec bool ` json:"dnsssec" `
Serial int64 ` json:"serial" `
Records [ ] Record ` json:"records,omitempty" `
Id string ` json:"id" `
Name string ` json:"name" `
URL string ` json:"url" `
Kind string ` json:"kind" `
DnsSec bool ` json:"dnsssec" `
Serial int64 ` json:"serial" `
Records [ ] Record ` json:"records,omitempty" `
ResourceRecordSets [ ] ResourceRecordSet ` json:"rrsets,omitempty" `
}
type Record struct {
Name string ` json:"name" `
Type string ` json:"type" `
Content string ` json:"content" `
TTL int ` json:"ttl" `
TTL int ` json:"ttl" ` // For API v0
Disabled bool ` json:"disabled" `
}
@ -80,6 +92,7 @@ type ResourceRecordSet struct {
Name string ` json:"name" `
Type string ` json:"type" `
ChangeType string ` json:"changetype" `
TTL int ` json:"ttl" ` // For API v1
Records [ ] Record ` json:"records,omitempty" `
}
@ -111,6 +124,26 @@ func parseId(recId string) (string, string, error) {
}
}
// Detects the API version in use on the server
// Uses int to represent the API version: 0 is the legacy AKA version 3.4 API
// Any other integer correlates with the same API version
func ( client * Client ) detectApiVersion ( ) ( int , error ) {
req , err := client . newRequest ( "GET" , "/api/v1/servers" , nil )
if err != nil {
return - 1 , err
}
resp , err := client . Http . Do ( req )
if err != nil {
return - 1 , err
}
defer resp . Body . Close ( )
if resp . StatusCode == 200 {
return 1 , nil
} else {
return 0 , nil
}
}
// Returns all Zones of server, without records
func ( client * Client ) ListZones ( ) ( [ ] ZoneInfo , error ) {
@ -154,7 +187,20 @@ func (client *Client) ListRecords(zone string) ([]Record, error) {
return nil , err
}
return zoneInfo . Records , nil
records := zoneInfo . Records
// Convert the API v1 response to v0 record structure
for _ , rrs := range zoneInfo . ResourceRecordSets {
for _ , record := range rrs . Records {
records = append ( records , Record {
Name : rrs . Name ,
Type : rrs . Type ,
Content : record . Content ,
TTL : rrs . TTL ,
} )
}
}
return records , nil
}
// Returns only records of specified name and type
@ -232,7 +278,7 @@ func (client *Client) CreateRecord(zone string, record Record) (string, error) {
}
defer resp . Body . Close ( )
if resp . StatusCode != 200 {
if resp . StatusCode != 200 && resp . StatusCode != 204 {
errorResp := new ( errorResponse )
if err = json . NewDecoder ( resp . Body ) . Decode ( errorResp ) ; err != nil {
return "" , fmt . Errorf ( "Error creating record: %s" , record . Id ( ) )
@ -263,7 +309,7 @@ func (client *Client) ReplaceRecordSet(zone string, rrSet ResourceRecordSet) (st
}
defer resp . Body . Close ( )
if resp . StatusCode != 200 {
if resp . StatusCode != 200 && resp . StatusCode != 204 {
errorResp := new ( errorResponse )
if err = json . NewDecoder ( resp . Body ) . Decode ( errorResp ) ; err != nil {
return "" , fmt . Errorf ( "Error creating record set: %s" , rrSet . Id ( ) )
@ -298,7 +344,7 @@ func (client *Client) DeleteRecordSet(zone string, name string, tpe string) erro
}
defer resp . Body . Close ( )
if resp . StatusCode != 200 {
if resp . StatusCode != 200 && resp . StatusCode != 204 {
errorResp := new ( errorResponse )
if err = json . NewDecoder ( resp . Body ) . Decode ( errorResp ) ; err != nil {
return fmt . Errorf ( "Error deleting record: %s %s" , name , tpe )