Merge pull request #4885 from hashicorp/alkersan-pdns

Rebased PowerDNS provider
This commit is contained in:
James Nugent 2016-01-28 14:25:15 -05:00
commit 932127e8c3
13 changed files with 1066 additions and 0 deletions

View File

@ -0,0 +1,12 @@
package main
import (
"github.com/hashicorp/terraform/builtin/providers/powerdns"
"github.com/hashicorp/terraform/plugin"
)
func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: powerdns.Provider,
})
}

View File

@ -0,0 +1 @@
package main

View File

@ -0,0 +1,319 @@
package powerdns
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"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
}
// NewClient returns a new PowerDNS client
func NewClient(serverUrl string, apiKey string) (*Client, error) {
client := Client{
ServerUrl: serverUrl,
ApiKey: apiKey,
Http: cleanhttp.DefaultClient(),
}
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)
if err != nil {
return nil, fmt.Errorf("Error during parting request URL: %s", err)
}
var bodyReader io.Reader
if body != nil {
bodyReader = bytes.NewReader(body)
}
req, err := http.NewRequest(method, url.String(), bodyReader)
if err != nil {
return nil, fmt.Errorf("Error during creation of request: %s", err)
}
req.Header.Add("X-API-Key", c.ApiKey)
req.Header.Add("Accept", "application/json")
if method != "GET" {
req.Header.Add("Content-Type", "application/json")
}
return req, nil
}
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"`
}
type Record struct {
Name string `json:"name"`
Type string `json:"type"`
Content string `json:"content"`
TTL int `json:"ttl"`
Disabled bool `json:"disabled"`
}
type ResourceRecordSet struct {
Name string `json:"name"`
Type string `json:"type"`
ChangeType string `json:"changetype"`
Records []Record `json:"records,omitempty"`
}
type zonePatchRequest struct {
RecordSets []ResourceRecordSet `json:"rrsets"`
}
type errorResponse struct {
ErrorMsg string `json:"error"`
}
func (record *Record) Id() string {
return fmt.Sprintf("%s-%s", record.Name, record.Type)
}
func (rrSet *ResourceRecordSet) Id() string {
return fmt.Sprintf("%s-%s", rrSet.Name, rrSet.Type)
}
// Returns name and type of record or record set based on it's ID
func parseId(recId string) (string, string, error) {
s := strings.Split(recId, "-")
if len(s) == 2 {
return s[0], s[1], nil
} else {
return "", "", fmt.Errorf("Unknown record ID format")
}
}
// Returns all Zones of server, without records
func (client *Client) ListZones() ([]ZoneInfo, error) {
req, err := client.newRequest("GET", "/servers/localhost/zones", nil)
if err != nil {
return nil, err
}
resp, err := client.Http.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var zoneInfos []ZoneInfo
err = json.NewDecoder(resp.Body).Decode(&zoneInfos)
if err != nil {
return nil, err
}
return zoneInfos, nil
}
// Returns all records in Zone
func (client *Client) ListRecords(zone string) ([]Record, error) {
req, err := client.newRequest("GET", fmt.Sprintf("/servers/localhost/zones/%s", zone), nil)
if err != nil {
return nil, err
}
resp, err := client.Http.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
zoneInfo := new(ZoneInfo)
err = json.NewDecoder(resp.Body).Decode(zoneInfo)
if err != nil {
return nil, err
}
return zoneInfo.Records, nil
}
// Returns only records of specified name and type
func (client *Client) ListRecordsInRRSet(zone string, name string, tpe string) ([]Record, error) {
allRecords, err := client.ListRecords(zone)
if err != nil {
return nil, err
}
records := make([]Record, 0, 10)
for _, r := range allRecords {
if r.Name == name && r.Type == tpe {
records = append(records, r)
}
}
return records, nil
}
func (client *Client) ListRecordsByID(zone string, recId string) ([]Record, error) {
name, tpe, err := parseId(recId)
if err != nil {
return nil, err
} else {
return client.ListRecordsInRRSet(zone, name, tpe)
}
}
// Checks if requested record exists in Zone
func (client *Client) RecordExists(zone string, name string, tpe string) (bool, error) {
allRecords, err := client.ListRecords(zone)
if err != nil {
return false, err
}
for _, record := range allRecords {
if record.Name == name && record.Type == tpe {
return true, nil
}
}
return false, nil
}
// Checks if requested record exists in Zone by it's ID
func (client *Client) RecordExistsByID(zone string, recId string) (bool, error) {
name, tpe, err := parseId(recId)
if err != nil {
return false, err
} else {
return client.RecordExists(zone, name, tpe)
}
}
// Creates new record with single content entry
func (client *Client) CreateRecord(zone string, record Record) (string, error) {
reqBody, _ := json.Marshal(zonePatchRequest{
RecordSets: []ResourceRecordSet{
{
Name: record.Name,
Type: record.Type,
ChangeType: "REPLACE",
Records: []Record{record},
},
},
})
req, err := client.newRequest("PATCH", fmt.Sprintf("/servers/localhost/zones/%s", zone), reqBody)
if err != nil {
return "", err
}
resp, err := client.Http.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
errorResp := new(errorResponse)
if err = json.NewDecoder(resp.Body).Decode(errorResp); err != nil {
return "", fmt.Errorf("Error creating record: %s", record.Id())
} else {
return "", fmt.Errorf("Error creating record: %s, reason: %q", record.Id(), errorResp.ErrorMsg)
}
} else {
return record.Id(), nil
}
}
// Creates new record set in Zone
func (client *Client) ReplaceRecordSet(zone string, rrSet ResourceRecordSet) (string, error) {
rrSet.ChangeType = "REPLACE"
reqBody, _ := json.Marshal(zonePatchRequest{
RecordSets: []ResourceRecordSet{rrSet},
})
req, err := client.newRequest("PATCH", fmt.Sprintf("/servers/localhost/zones/%s", zone), reqBody)
if err != nil {
return "", err
}
resp, err := client.Http.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
errorResp := new(errorResponse)
if err = json.NewDecoder(resp.Body).Decode(errorResp); err != nil {
return "", fmt.Errorf("Error creating record set: %s", rrSet.Id())
} else {
return "", fmt.Errorf("Error creating record set: %s, reason: %q", rrSet.Id(), errorResp.ErrorMsg)
}
} else {
return rrSet.Id(), nil
}
}
// Deletes record set from Zone
func (client *Client) DeleteRecordSet(zone string, name string, tpe string) error {
reqBody, _ := json.Marshal(zonePatchRequest{
RecordSets: []ResourceRecordSet{
{
Name: name,
Type: tpe,
ChangeType: "DELETE",
},
},
})
req, err := client.newRequest("PATCH", fmt.Sprintf("/servers/localhost/zones/%s", zone), reqBody)
if err != nil {
return err
}
resp, err := client.Http.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
errorResp := new(errorResponse)
if err = json.NewDecoder(resp.Body).Decode(errorResp); err != nil {
return fmt.Errorf("Error deleting record: %s %s", name, tpe)
} else {
return fmt.Errorf("Error deleting record: %s %s, reason: %q", name, tpe, errorResp.ErrorMsg)
}
} else {
return nil
}
}
// Deletes record from Zone by it's ID
func (client *Client) DeleteRecordSetByID(zone string, recId string) error {
name, tpe, err := parseId(recId)
if err != nil {
return err
} else {
return client.DeleteRecordSet(zone, name, tpe)
}
}

View File

@ -0,0 +1,24 @@
package powerdns
import (
"fmt"
"log"
)
type Config struct {
ServerUrl string
ApiKey string
}
// Client returns a new client for accessing PowerDNS
func (c *Config) Client() (*Client, error) {
client, err := NewClient(c.ServerUrl, c.ApiKey)
if err != nil {
return nil, fmt.Errorf("Error setting up PowerDNS client: %s", err)
}
log.Printf("[INFO] PowerDNS Client configured for server %s", c.ServerUrl)
return client, nil
}

View File

@ -0,0 +1,52 @@
package powerdns
import (
"os"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
func Provider() terraform.ResourceProvider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
"api_key": {
Type: schema.TypeString,
Required: true,
DefaultFunc: envDefaultFunc("PDNS_API_KEY"),
Description: "REST API authentication key",
},
"server_url": {
Type: schema.TypeString,
Required: true,
DefaultFunc: envDefaultFunc("PDNS_SERVER_URL"),
Description: "Location of PowerDNS server",
},
},
ResourcesMap: map[string]*schema.Resource{
"powerdns_record": resourcePDNSRecord(),
},
ConfigureFunc: providerConfigure,
}
}
func providerConfigure(data *schema.ResourceData) (interface{}, error) {
config := Config{
ApiKey: data.Get("api_key").(string),
ServerUrl: data.Get("server_url").(string),
}
return config.Client()
}
func envDefaultFunc(k string) schema.SchemaDefaultFunc {
return func() (interface{}, error) {
if v := os.Getenv(k); v != "" {
return v, nil
}
return nil, nil
}
}

View File

@ -0,0 +1,39 @@
package powerdns
import (
"os"
"testing"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
var testAccProviders map[string]terraform.ResourceProvider
var testAccProvider *schema.Provider
func init() {
testAccProvider = Provider().(*schema.Provider)
testAccProviders = map[string]terraform.ResourceProvider{
"powerdns": testAccProvider,
}
}
func TestProvider(t *testing.T) {
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProviderImpl(t *testing.T) {
var _ terraform.ResourceProvider = Provider()
}
func testAccPreCheck(t *testing.T) {
if v := os.Getenv("PDNS_API_KEY"); v == "" {
t.Fatal("PDNS_API_KEY must be set for acceptance tests")
}
if v := os.Getenv("PDNS_SERVER_URL"); v == "" {
t.Fatal("PDNS_SERVER_URL must be set for acceptance tests")
}
}

View File

@ -0,0 +1,146 @@
package powerdns
import (
"log"
"fmt"
"github.com/hashicorp/terraform/helper/schema"
)
func resourcePDNSRecord() *schema.Resource {
return &schema.Resource{
Create: resourcePDNSRecordCreate,
Read: resourcePDNSRecordRead,
Delete: resourcePDNSRecordDelete,
Exists: resourcePDNSRecordExists,
Schema: map[string]*schema.Schema{
"zone": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"type": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"ttl": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"records": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Required: true,
ForceNew: true,
Set: schema.HashString,
},
},
}
}
func resourcePDNSRecordCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Client)
rrSet := ResourceRecordSet{
Name: d.Get("name").(string),
Type: d.Get("type").(string),
}
zone := d.Get("zone").(string)
ttl := d.Get("ttl").(int)
recs := d.Get("records").(*schema.Set).List()
if len(recs) > 0 {
records := make([]Record, 0, len(recs))
for _, recContent := range recs {
records = append(records, Record{Name: rrSet.Name, Type: rrSet.Type, TTL: ttl, Content: recContent.(string)})
}
rrSet.Records = records
log.Printf("[DEBUG] Creating PowerDNS Record: %#v", rrSet)
recId, err := client.ReplaceRecordSet(zone, rrSet)
if err != nil {
return fmt.Errorf("Failed to create PowerDNS Record: %s", err)
}
d.SetId(recId)
log.Printf("[INFO] Created PowerDNS Record with ID: %s", d.Id())
} else {
log.Printf("[DEBUG] Deleting empty PowerDNS Record: %#v", rrSet)
err := client.DeleteRecordSet(zone, rrSet.Name, rrSet.Type)
if err != nil {
return fmt.Errorf("Failed to delete PowerDNS Record: %s", err)
}
d.SetId(rrSet.Id())
}
return resourcePDNSRecordRead(d, meta)
}
func resourcePDNSRecordRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Client)
log.Printf("[DEBUG] Reading PowerDNS Record: %s", d.Id())
records, err := client.ListRecordsByID(d.Get("zone").(string), d.Id())
if err != nil {
return fmt.Errorf("Couldn't fetch PowerDNS Record: %s", err)
}
recs := make([]string, 0, len(records))
for _, r := range records {
recs = append(recs, r.Content)
}
d.Set("records", recs)
if len(records) > 0 {
d.Set("ttl", records[0].TTL)
}
return nil
}
func resourcePDNSRecordDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Client)
log.Printf("[INFO] Deleting PowerDNS Record: %s", d.Id())
err := client.DeleteRecordSetByID(d.Get("zone").(string), d.Id())
if err != nil {
return fmt.Errorf("Error deleting PowerDNS Record: %s", err)
}
return nil
}
func resourcePDNSRecordExists(d *schema.ResourceData, meta interface{}) (bool, error) {
zone := d.Get("zone").(string)
name := d.Get("name").(string)
tpe := d.Get("type").(string)
log.Printf("[INFO] Checking existence of PowerDNS Record: %s, %s", name, tpe)
client := meta.(*Client)
exists, err := client.RecordExists(zone, name, tpe)
if err != nil {
return false, fmt.Errorf("Error checking PowerDNS Record: %s", err)
} else {
return exists, nil
}
}

View File

@ -0,0 +1,371 @@
package powerdns
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccPDNSRecord_A(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPDNSRecordDestroy,
Steps: []resource.TestStep{
{
Config: testPDNSRecordConfigA,
Check: resource.ComposeTestCheckFunc(
testAccCheckPDNSRecordExists("powerdns_record.test-a"),
),
},
},
})
}
func TestAccPDNSRecord_AAAA(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPDNSRecordDestroy,
Steps: []resource.TestStep{
{
Config: testPDNSRecordConfigAAAA,
Check: resource.ComposeTestCheckFunc(
testAccCheckPDNSRecordExists("powerdns_record.test-aaaa"),
),
},
},
})
}
func TestAccPDNSRecord_CNAME(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPDNSRecordDestroy,
Steps: []resource.TestStep{
{
Config: testPDNSRecordConfigCNAME,
Check: resource.ComposeTestCheckFunc(
testAccCheckPDNSRecordExists("powerdns_record.test-cname"),
),
},
},
})
}
func TestAccPDNSRecord_HINFO(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPDNSRecordDestroy,
Steps: []resource.TestStep{
{
Config: testPDNSRecordConfigHINFO,
Check: resource.ComposeTestCheckFunc(
testAccCheckPDNSRecordExists("powerdns_record.test-hinfo"),
),
},
},
})
}
func TestAccPDNSRecord_LOC(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPDNSRecordDestroy,
Steps: []resource.TestStep{
{
Config: testPDNSRecordConfigLOC,
Check: resource.ComposeTestCheckFunc(
testAccCheckPDNSRecordExists("powerdns_record.test-loc"),
),
},
},
})
}
func TestAccPDNSRecord_MX(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPDNSRecordDestroy,
Steps: []resource.TestStep{
{
Config: testPDNSRecordConfigMX,
Check: resource.ComposeTestCheckFunc(
testAccCheckPDNSRecordExists("powerdns_record.test-mx"),
),
},
{
Config: testPDNSRecordConfigMXMulti,
Check: resource.ComposeTestCheckFunc(
testAccCheckPDNSRecordExists("powerdns_record.test-mx-multi"),
),
},
},
})
}
func TestAccPDNSRecord_NAPTR(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPDNSRecordDestroy,
Steps: []resource.TestStep{
{
Config: testPDNSRecordConfigNAPTR,
Check: resource.ComposeTestCheckFunc(
testAccCheckPDNSRecordExists("powerdns_record.test-naptr"),
),
},
},
})
}
func TestAccPDNSRecord_NS(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPDNSRecordDestroy,
Steps: []resource.TestStep{
{
Config: testPDNSRecordConfigNS,
Check: resource.ComposeTestCheckFunc(
testAccCheckPDNSRecordExists("powerdns_record.test-ns"),
),
},
},
})
}
func TestAccPDNSRecord_SPF(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPDNSRecordDestroy,
Steps: []resource.TestStep{
{
Config: testPDNSRecordConfigSPF,
Check: resource.ComposeTestCheckFunc(
testAccCheckPDNSRecordExists("powerdns_record.test-spf"),
),
},
},
})
}
func TestAccPDNSRecord_SSHFP(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPDNSRecordDestroy,
Steps: []resource.TestStep{
{
Config: testPDNSRecordConfigSSHFP,
Check: resource.ComposeTestCheckFunc(
testAccCheckPDNSRecordExists("powerdns_record.test-sshfp"),
),
},
},
})
}
func TestAccPDNSRecord_SRV(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPDNSRecordDestroy,
Steps: []resource.TestStep{
{
Config: testPDNSRecordConfigSRV,
Check: resource.ComposeTestCheckFunc(
testAccCheckPDNSRecordExists("powerdns_record.test-srv"),
),
},
},
})
}
func TestAccPDNSRecord_TXT(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPDNSRecordDestroy,
Steps: []resource.TestStep{
{
Config: testPDNSRecordConfigTXT,
Check: resource.ComposeTestCheckFunc(
testAccCheckPDNSRecordExists("powerdns_record.test-txt"),
),
},
},
})
}
func testAccCheckPDNSRecordDestroy(s *terraform.State) error {
for _, rs := range s.RootModule().Resources {
if rs.Type != "powerdns_record" {
continue
}
client := testAccProvider.Meta().(*Client)
exists, err := client.RecordExistsByID(rs.Primary.Attributes["zone"], rs.Primary.ID)
if err != nil {
return fmt.Errorf("Error checking if record still exists: %#v", rs.Primary.ID)
}
if exists {
return fmt.Errorf("Record still exists: %#v", rs.Primary.ID)
}
}
return nil
}
func testAccCheckPDNSRecordExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No Record ID is set")
}
client := testAccProvider.Meta().(*Client)
foundRecords, err := client.ListRecordsByID(rs.Primary.Attributes["zone"], rs.Primary.ID)
if err != nil {
return err
}
if len(foundRecords) == 0 {
return fmt.Errorf("Record does not exist")
}
for _, rec := range foundRecords {
if rec.Id() == rs.Primary.ID {
return nil
}
}
return fmt.Errorf("Record does not exist: %#v", rs.Primary.ID)
}
}
const testPDNSRecordConfigA = `
resource "powerdns_record" "test-a" {
zone = "sysa.xyz"
name = "redis.sysa.xyz"
type = "A"
ttl = 60
records = [ "1.1.1.1", "2.2.2.2" ]
}`
const testPDNSRecordConfigAAAA = `
resource "powerdns_record" "test-aaaa" {
zone = "sysa.xyz"
name = "redis.sysa.xyz"
type = "AAAA"
ttl = 60
records = [ "2001:DB8:2000:bf0::1", "2001:DB8:2000:bf1::1" ]
}`
const testPDNSRecordConfigCNAME = `
resource "powerdns_record" "test-cname" {
zone = "sysa.xyz"
name = "redis.sysa.xyz"
type = "CNAME"
ttl = 60
records = [ "redis.example.com" ]
}`
const testPDNSRecordConfigHINFO = `
resource "powerdns_record" "test-hinfo" {
zone = "sysa.xyz"
name = "redis.sysa.xyz"
type = "HINFO"
ttl = 60
records = [ "\"PC-Intel-2.4ghz\" \"Linux\"" ]
}`
const testPDNSRecordConfigLOC = `
resource "powerdns_record" "test-loc" {
zone = "sysa.xyz"
name = "redis.sysa.xyz"
type = "LOC"
ttl = 60
records = [ "51 56 0.123 N 5 54 0.000 E 4.00m 1.00m 10000.00m 10.00m" ]
}`
const testPDNSRecordConfigMX = `
resource "powerdns_record" "test-mx" {
zone = "sysa.xyz"
name = "sysa.xyz"
type = "MX"
ttl = 60
records = [ "10 mail.example.com" ]
}`
const testPDNSRecordConfigMXMulti = `
resource "powerdns_record" "test-mx-multi" {
zone = "sysa.xyz"
name = "sysa.xyz"
type = "MX"
ttl = 60
records = [ "10 mail1.example.com", "20 mail2.example.com" ]
}`
const testPDNSRecordConfigNAPTR = `
resource "powerdns_record" "test-naptr" {
zone = "sysa.xyz"
name = "sysa.xyz"
type = "NAPTR"
ttl = 60
records = [ "100 50 \"s\" \"z3950+I2L+I2C\" \"\" _z3950._tcp.gatech.edu'." ]
}`
const testPDNSRecordConfigNS = `
resource "powerdns_record" "test-ns" {
zone = "sysa.xyz"
name = "lab.sysa.xyz"
type = "NS"
ttl = 60
records = [ "ns1.sysa.xyz", "ns2.sysa.xyz" ]
}`
const testPDNSRecordConfigSPF = `
resource "powerdns_record" "test-spf" {
zone = "sysa.xyz"
name = "sysa.xyz"
type = "SPF"
ttl = 60
records = [ "\"v=spf1 +all\"" ]
}`
const testPDNSRecordConfigSSHFP = `
resource "powerdns_record" "test-sshfp" {
zone = "sysa.xyz"
name = "ssh.sysa.xyz"
type = "SSHFP"
ttl = 60
records = [ "1 1 123456789abcdef67890123456789abcdef67890" ]
}`
const testPDNSRecordConfigSRV = `
resource "powerdns_record" "test-srv" {
zone = "sysa.xyz"
name = "_redis._tcp.sysa.xyz"
type = "SRV"
ttl = 60
records = [ "0 10 6379 redis1.sysa.xyz", "0 10 6379 redis2.sysa.xyz", "10 10 6379 redis-replica.sysa.xyz" ]
}`
const testPDNSRecordConfigTXT = `
resource "powerdns_record" "test-txt" {
zone = "sysa.xyz"
name = "text.sysa.xyz"
type = "TXT"
ttl = 60
records = [ "\"text record payload\"" ]
}`

View File

@ -26,6 +26,7 @@ body.layout-mysql,
body.layout-openstack,
body.layout-packet,
body.layout-postgresql,
body.layout-powerdns,
body.layout-rundeck,
body.layout-statuscake,
body.layout-template,

View File

@ -0,0 +1,36 @@
---
layout: "powerdns"
page_title: "Provider: PowerDNS"
sidebar_current: "docs-powerdns-index"
description: |-
The PowerDNS provider is used manipulate DNS records supported by PowerDNS server. The provider needs to be configured with the proper credentials before it can be used.
---
# PowerDNS Provider
The PowerDNS provider is used manipulate DNS records supported by PowerDNS server. The provider needs to be configured
with the proper credentials before it can be used.
Use the navigation to the left to read about the available resources.
## Example Usage
```
# Configure the PowerDNS provider
provider "powerdns" {
api_key = "${var.pdns_api_key}"
server_url = "${var.pdns_server_url}"
}
# Create a record
resource "powerdns_record" "www" {
...
}
```
## Argument Reference
The following arguments are supported:
* `api_key` - (Required) The PowerDNS API key. This can also be specified with `PDNS_API_KEY` environment variable.
* `server_url` - (Required) The address of PowerDNS server. This can also be specified with `PDNS_SERVER_URL` environment variable.

View File

@ -0,0 +1,35 @@
---
layout: "powerdns"
page_title: "PowerDNS: powerdns_record"
sidebar_current: "docs-powerdns-resource-record"
description: |-
Provides a PowerDNS record resource.
---
# powerdns\_record
Provides a PowerDNS record resource.
## Example Usage
```
# Add a record to the zone
resource "powerdns_record" "foobar" {
zone = "example.com"
name = "www.example.com"
type = "A"
ttl = 300
records = ["192.168.0.11"]
}
```
## Argument Reference
The following arguments are supported:
* `zone` - (Required) The name of zone to contain this record.
* `name` - (Required) The name of the record.
* `type` - (Required) The record type.
* `ttl` - (Required) The TTL of the record.
* `records` - (Required) A string list of records.

View File

@ -201,6 +201,10 @@
<a href="/docs/providers/postgresql/index.html">PostgreSQL</a>
</li>
<li<%= sidebar_current("docs-providers-powerdns") %>>
<a href="/docs/providers/powerdns/index.html">PowerDNS</a>
</li>
<li<%= sidebar_current("docs-providers-rundeck") %>>
<a href="/docs/providers/rundeck/index.html">Rundeck</a>
</li>

View File

@ -0,0 +1,26 @@
<% wrap_layout :inner do %>
<% content_for :sidebar do %>
<div class="docs-sidebar hidden-print affix-top" role="complementary">
<ul class="nav docs-sidenav">
<li<%= sidebar_current("docs-home") %>>
<a href="/docs/providers/index.html">&laquo; Documentation Home</a>
</li>
<li<%= sidebar_current("docs-powerdns-index") %>>
<a href="/docs/providers/powerdns/index.html">PowerDNS Provider</a>
</li>
<li<%= sidebar_current(/^docs-powerdns-resource/) %>>
<a href="#">Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-powerdns-resource-record") %>>
<a href="/docs/providers/powerdns/r/record.html">powerdns_record</a>
</li>
</ul>
</li>
</ul>
</div>
<% end %>
<%= yield %>
<% end %>