From 0f991b08a520612e58e8c00b117e1764cc57bb9d Mon Sep 17 00:00:00 2001 From: Sergey Yakovlev Date: Wed, 19 May 2021 23:47:42 +0300 Subject: [PATCH 1/8] Add resource_powerdns_zone_metadata --- powerdns/client.go | 141 +++++++++++++++++++- powerdns/provider.go | 5 +- powerdns/resource_powerdns_zone_metadata.go | 120 +++++++++++++++++ 3 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 powerdns/resource_powerdns_zone_metadata.go diff --git a/powerdns/client.go b/powerdns/client.go index 78783dae..2c1b3b7c 100644 --- a/powerdns/client.go +++ b/powerdns/client.go @@ -185,6 +185,12 @@ type ResourceRecordSet struct { Records []Record `json:"records,omitempty"` } +// ResourceMetadataSet represents a PowerDNS Zone Metadata object +type ResourceZoneMetadata struct { + Kind string `json:"kind"` + Metadata []string `json:"metadata"` +} + type zonePatchRequest struct { RecordSets []ResourceRecordSet `json:"rrsets"` } @@ -215,6 +221,11 @@ func (rrSet *ResourceRecordSet) ID() string { return rrSet.Name + idSeparator + rrSet.Type } +// ID returns a zoneMetadata with the ID format +func (metadata *ResourceZoneMetadata) ID(zone string) string { + return zone + idSeparator + metadata.Kind +} + // Returns name and type of record or record set based on its ID func parseID(recID string) (string, string, error) { s := strings.Split(recID, idSeparator) @@ -292,7 +303,7 @@ func (client *Client) GetZone(name string) (ZoneInfo, error) { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { + if resp.StatusCode != 200 { errorResp := new(errorResponse) if err = json.NewDecoder(resp.Body).Decode(errorResp); err != nil { return ZoneInfo{}, fmt.Errorf("Error getting zone: %s", name) @@ -576,6 +587,134 @@ func (client *Client) DeleteRecordSetByID(zone string, recID string) error { return client.DeleteRecordSet(zone, name, tpe) } +func (client *Client) GetZoneMetadata(id string) (ResourceZoneMetadata, error) { + zone, kind, err := parseID(id) + if err != nil { + return ResourceZoneMetadata{}, err + } + + req, err := client.newRequest("GET", fmt.Sprintf("/servers/localhost/zones/%s/metadata/%s", zone, kind), nil) + if err != nil { + return ResourceZoneMetadata{}, err + } + + resp, err := client.HTTP.Do(req) + if err != nil { + return ResourceZoneMetadata{}, err + } + defer resp.Body.Close() + + var zoneMetadata ResourceZoneMetadata + err = json.NewDecoder(resp.Body).Decode(&zoneMetadata) + if err != nil { + return ResourceZoneMetadata{}, err + } + + return zoneMetadata, nil +} + +// UpdateZoneMetadata creates new record set in Zone +func (client *Client) UpdateZoneMetadata(zone string, zoneMetadata ResourceZoneMetadata) (string, error) { + body, err := json.Marshal(zoneMetadata) + if err != nil { + return "", err + } + + req, err := client.newRequest("PUT", fmt.Sprintf("/servers/localhost/zones/%s/metadata/%s", zone, zoneMetadata.Kind), body) + if err != nil { + return "", err + } + + resp, err := client.HTTP.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + errorResp := new(errorResponse) + if err = json.NewDecoder(resp.Body).Decode(errorResp); err != nil { + return "", fmt.Errorf("Error updating zone metadata: %s", zoneMetadata.Kind) + } + return "", fmt.Errorf("Error updating zone metadata: %s, reason: %q", zoneMetadata.Kind, errorResp.ErrorMsg) + } + + var createdZoneMetadata ResourceZoneMetadata + err = json.NewDecoder(resp.Body).Decode(&createdZoneMetadata) + if err != nil { + return "", err + } + + return createdZoneMetadata.Kind, nil +} + +func (client *Client) DeleteZoneMetadata(id string) error { + zone, kind, err := parseID(id) + if err != nil { + return err + } + + req, err := client.newRequest("DELETE", fmt.Sprintf("/servers/localhost/zones/%s/metadata/%s", zone, kind), nil) + 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 Zone Metadata: %s", kind) + } + return fmt.Errorf("Error deleting Zone Metadata: %s, reason: %q", kind, errorResp.ErrorMsg) + } + return nil +} + +// ZoneMetadataExists checks if requested zone exists +func (client *Client) ZoneMetadataExists(id string) (bool, error) { + zone, kind, err := parseID(id) + if err != nil { + return false, err + } + + req, err := client.newRequest("GET", fmt.Sprintf("/servers/localhost/zones/%s/metadata/%s", zone, kind), nil) + if err != nil { + return false, err + } + + resp, err := client.HTTP.Do(req) + if err != nil { + return false, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound { + errorResp := new(errorResponse) + if err = json.NewDecoder(resp.Body).Decode(errorResp); err != nil { + return false, fmt.Errorf("Error getting Zone Metadata: %s", kind) + } + return false, fmt.Errorf("Error getting Zone Metadata: %s, reason: %q", kind, errorResp.ErrorMsg) + } + + var ZoneMetadata ResourceZoneMetadata + err = json.NewDecoder(resp.Body).Decode(&ZoneMetadata) + + if err != nil { + return false, err + } + + if len(ZoneMetadata.Metadata) == 0 { + return false, err + } + + return resp.StatusCode == http.StatusOK, nil +} + func (client *Client) setServerVersion() error { req, err := client.newRequest("GET", "/servers/localhost", nil) if err != nil { diff --git a/powerdns/provider.go b/powerdns/provider.go index c505b025..6845759a 100644 --- a/powerdns/provider.go +++ b/powerdns/provider.go @@ -36,8 +36,9 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "powerdns_zone": resourcePDNSZone(), - "powerdns_record": resourcePDNSRecord(), + "powerdns_zone": resourcePDNSZone(), + "powerdns_zone_metadata": resourcePDNSZoneMetadata(), + "powerdns_record": resourcePDNSRecord(), }, ConfigureFunc: providerConfigure, diff --git a/powerdns/resource_powerdns_zone_metadata.go b/powerdns/resource_powerdns_zone_metadata.go new file mode 100644 index 00000000..68a6c80d --- /dev/null +++ b/powerdns/resource_powerdns_zone_metadata.go @@ -0,0 +1,120 @@ +package powerdns + +import ( + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourcePDNSZoneMetadata() *schema.Resource { + return &schema.Resource{ + Create: resourcePDNSZoneMetadataCreate, + Read: resourcePDNSZoneMetadataRead, + Delete: resourcePDNSZoneMetadataDelete, + Exists: resourcePDNSZoneMetadataExists, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "zone": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "kind": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "metadata": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Required: true, + ForceNew: false, + }, + }, + } +} + +func resourcePDNSZoneMetadataCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Client) + + zone := d.Get("zone").(string) + mtdata := d.Get("metadata").(*schema.Set).List() + + for _, mt := range mtdata { + if len(strings.Trim(mt.(string), " ")) == 0 { + log.Printf("[WARN] One or more values in 'metadata' contain empty '' value(s)") + } + } + if !(len(mtdata) > 0) { + return fmt.Errorf("'metadata' must not be empty") + } + + metadata := make([]string, 0, len(mtdata)) + for _, mt := range mtdata { + metadata = append(metadata, mt.(string)) + } + + zoneMetadata := ResourceZoneMetadata{ + Kind: d.Get("kind").(string), + Metadata: metadata, + } + + log.Printf("[DEBUG] Creating PowerDNS Zone Metadata: %#v", zoneMetadata) + + metaid, err := client.UpdateZoneMetadata(zone, zoneMetadata) + if err != nil { + return fmt.Errorf("Failed to create PowerDNS Zone Metadata: %s", err) + } + + d.SetId(metaid) + log.Printf("[INFO] Created PowerDNS Zone Metadata with ID: %s", d.Id()) + + return nil +} + +func resourcePDNSZoneMetadataRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Client) + + log.Printf("[DEBUG] Reading PowerDNS Zone Metadata: %s", d.Id()) + record, err := client.GetZoneMetadata(d.Id()) + if err != nil { + return fmt.Errorf("Couldn't fetch PowerDNS Zone Metadata: %s", err) + } + + d.Set("kind", record.Kind) + d.Set("metadata", record.Metadata) + + return nil +} + +func resourcePDNSZoneMetadataDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Client) + + log.Printf("[INFO] Deleting PowerDNS Zone Metadata: %s", d.Id()) + err := client.DeleteZoneMetadata(d.Id()) + + if err != nil { + return fmt.Errorf("Error deleting PowerDNS Zone Metadata: %s", err) + } + + return nil +} + +func resourcePDNSZoneMetadataExists(d *schema.ResourceData, meta interface{}) (bool, error) { + log.Printf("[INFO] Checking existence of PowerDNS Zone Metadata: %s", d.Id()) + + client := meta.(*Client) + exists, err := client.ZoneMetadataExists(d.Id()) + + if err != nil { + return false, fmt.Errorf("Error checking PowerDNS Zone Metadata: %s", err) + } + return exists, nil +} From c4279793e98e58f638431ef44f329c7fc0148671 Mon Sep 17 00:00:00 2001 From: Sergey Yakovlev Date: Thu, 20 May 2021 00:32:32 +0300 Subject: [PATCH 2/8] Fix linting and add basic tests --- .gitignore | 2 +- powerdns/client.go | 4 +- .../resource_powerdns_zone_metadata_test.go | 80 +++++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 powerdns/resource_powerdns_zone_metadata_test.go diff --git a/.gitignore b/.gitignore index 92a74ee3..49c26497 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -terraform-provider-dns +terraform-provider-powerdns *.dll *.exe diff --git a/powerdns/client.go b/powerdns/client.go index 2c1b3b7c..8c2c1465 100644 --- a/powerdns/client.go +++ b/powerdns/client.go @@ -185,7 +185,7 @@ type ResourceRecordSet struct { Records []Record `json:"records,omitempty"` } -// ResourceMetadataSet represents a PowerDNS Zone Metadata object +// ResourceZoneMetadata represents a PowerDNS Zone Metadata object type ResourceZoneMetadata struct { Kind string `json:"kind"` Metadata []string `json:"metadata"` @@ -587,6 +587,7 @@ func (client *Client) DeleteRecordSetByID(zone string, recID string) error { return client.DeleteRecordSet(zone, name, tpe) } +// GetZoneMetadata get metadata from zone by its ID func (client *Client) GetZoneMetadata(id string) (ResourceZoneMetadata, error) { zone, kind, err := parseID(id) if err != nil { @@ -648,6 +649,7 @@ func (client *Client) UpdateZoneMetadata(zone string, zoneMetadata ResourceZoneM return createdZoneMetadata.Kind, nil } +// DeleteZoneMetadata deletes zone metadata by its ID func (client *Client) DeleteZoneMetadata(id string) error { zone, kind, err := parseID(id) if err != nil { diff --git a/powerdns/resource_powerdns_zone_metadata_test.go b/powerdns/resource_powerdns_zone_metadata_test.go new file mode 100644 index 00000000..18e90373 --- /dev/null +++ b/powerdns/resource_powerdns_zone_metadata_test.go @@ -0,0 +1,80 @@ +package powerdns + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccPDNSZoneMetadata_Empty(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testPDNSZoneMetadataEmpty, + ExpectError: regexp.MustCompile("'metadata' must not be empty"), + }, + }, + }) +} + +func testAccCheckPDNSZoneMetadataDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "powerdns_zone_metadata" { + 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 testAccCheckPDNSZoneMetadataExists(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 testPDNSZoneMetadataEmpty = ` +resource "powerdns_zone_metadata" "test-a" { + zone = "sysa.xyz." + kind = "ALLOW-AXFR-FROM" + metadata = [ ] +}` + + + From 0363bd3ddce3bfc5ef7749a61df7ca62c7024281 Mon Sep 17 00:00:00 2001 From: Sergey Yakovlev Date: Thu, 20 May 2021 11:19:37 +0300 Subject: [PATCH 3/8] Change ForceNew to true in all situation --- docker-compose.yml | 2 +- powerdns/resource_powerdns_zone_metadata.go | 4 ++-- powerdns/resource_powerdns_zone_metadata_test.go | 3 --- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7040d96b..79c23758 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: "3" +version: "3.7" services: nginx: image: nginx:1.17.2 diff --git a/powerdns/resource_powerdns_zone_metadata.go b/powerdns/resource_powerdns_zone_metadata.go index 68a6c80d..3dbfbedd 100644 --- a/powerdns/resource_powerdns_zone_metadata.go +++ b/powerdns/resource_powerdns_zone_metadata.go @@ -28,14 +28,14 @@ func resourcePDNSZoneMetadata() *schema.Resource { "kind": { Type: schema.TypeString, Required: true, - ForceNew: false, + ForceNew: true, }, "metadata": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Required: true, - ForceNew: false, + ForceNew: true, }, }, } diff --git a/powerdns/resource_powerdns_zone_metadata_test.go b/powerdns/resource_powerdns_zone_metadata_test.go index 18e90373..54b842e2 100644 --- a/powerdns/resource_powerdns_zone_metadata_test.go +++ b/powerdns/resource_powerdns_zone_metadata_test.go @@ -75,6 +75,3 @@ resource "powerdns_zone_metadata" "test-a" { kind = "ALLOW-AXFR-FROM" metadata = [ ] }` - - - From 68b8d6148519f1f563542891f4036f6413c28c47 Mon Sep 17 00:00:00 2001 From: Sergey Yakovlev Date: Thu, 20 May 2021 15:26:50 +0300 Subject: [PATCH 4/8] Add skips --- powerdns/resource_powerdns_zone_test.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/powerdns/resource_powerdns_zone_test.go b/powerdns/resource_powerdns_zone_test.go index 0e2b4e95..43aafd0d 100644 --- a/powerdns/resource_powerdns_zone_test.go +++ b/powerdns/resource_powerdns_zone_test.go @@ -11,6 +11,7 @@ import ( func TestAccPDNSZoneNative(t *testing.T) { resourceName := "powerdns_zone.test-native" + t.Skip("skip") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -82,6 +83,7 @@ func TestAccPDNSZoneNativeSmallCaps(t *testing.T) { func TestAccPDNSZoneMaster(t *testing.T) { resourceName := "powerdns_zone.test-master" + t.Skip("skip") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -108,6 +110,7 @@ func TestAccPDNSZoneMaster(t *testing.T) { func TestAccPDNSZoneMasterSOAAPIEDIT(t *testing.T) { resourceName := "powerdns_zone.test-master-soa-edit-api" resourceSOAEDITAPI := `DEFAULT` + t.Skip("skip") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -135,6 +138,7 @@ func TestAccPDNSZoneMasterSOAAPIEDIT(t *testing.T) { func TestAccPDNSZoneMasterSOAAPIEDITEmpty(t *testing.T) { resourceName := "powerdns_zone.test-master-soa-edit-api-empty" resourceSOAEDITAPI := `""` + t.Skip("skip") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -165,7 +169,7 @@ func TestAccPDNSZoneMasterSOAAPIEDITUndefined(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckPDNSZoneDestroy, + CheckDestroy: testAccCheckPDNSZoneDestroy, Steps: []resource.TestStep{ { Config: testPDNSZoneConfigMasterSOAEDITAPIUndefined, @@ -187,6 +191,7 @@ func TestAccPDNSZoneMasterSOAAPIEDITUndefined(t *testing.T) { func TestAccPDNSZoneAccount(t *testing.T) { resourceName := "powerdns_zone.test-account" resourceAccount := `test` + t.Skip("skip") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -214,6 +219,7 @@ func TestAccPDNSZoneAccount(t *testing.T) { func TestAccPDNSZoneAccountEmpty(t *testing.T) { resourceName := "powerdns_zone.test-account-empty" resourceAccount := `` + t.Skip("skip") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -241,6 +247,7 @@ func TestAccPDNSZoneAccountEmpty(t *testing.T) { func TestAccPDNSZoneAccountUndefined(t *testing.T) { resourceName := "powerdns_zone.test-account-undefined" resourceAccount := `admin` + t.Skip("skip") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, From cd81ddd7e7532064a576b29addd38ad683b32267 Mon Sep 17 00:00:00 2001 From: Sergey Yakovlev Date: Thu, 20 May 2021 20:04:45 +0300 Subject: [PATCH 5/8] Add tests --- powerdns/client.go | 2 +- powerdns/resource_powerdns_zone_metadata.go | 7 + .../resource_powerdns_zone_metadata_test.go | 132 +++++++++++++----- powerdns/resource_powerdns_zone_test.go | 65 ++++----- 4 files changed, 138 insertions(+), 68 deletions(-) diff --git a/powerdns/client.go b/powerdns/client.go index 8c2c1465..1aa72283 100644 --- a/powerdns/client.go +++ b/powerdns/client.go @@ -646,7 +646,7 @@ func (client *Client) UpdateZoneMetadata(zone string, zoneMetadata ResourceZoneM return "", err } - return createdZoneMetadata.Kind, nil + return createdZoneMetadata.ID(zone), nil } // DeleteZoneMetadata deletes zone metadata by its ID diff --git a/powerdns/resource_powerdns_zone_metadata.go b/powerdns/resource_powerdns_zone_metadata.go index 3dbfbedd..168f95e4 100644 --- a/powerdns/resource_powerdns_zone_metadata.go +++ b/powerdns/resource_powerdns_zone_metadata.go @@ -88,8 +88,15 @@ func resourcePDNSZoneMetadataRead(d *schema.ResourceData, meta interface{}) erro return fmt.Errorf("Couldn't fetch PowerDNS Zone Metadata: %s", err) } + zone, _, err := parseID(d.Id()) + if err != nil { + return err + } + + d.SetId(d.Id()) d.Set("kind", record.Kind) d.Set("metadata", record.Metadata) + d.Set("zone", zone) return nil } diff --git a/powerdns/resource_powerdns_zone_metadata_test.go b/powerdns/resource_powerdns_zone_metadata_test.go index 54b842e2..a4a5eb54 100644 --- a/powerdns/resource_powerdns_zone_metadata_test.go +++ b/powerdns/resource_powerdns_zone_metadata_test.go @@ -15,63 +15,125 @@ func TestAccPDNSZoneMetadata_Empty(t *testing.T) { Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testPDNSZoneMetadataEmpty, + Config: testPDNSZoneMetadata_Empty, ExpectError: regexp.MustCompile("'metadata' must not be empty"), }, }, }) } +func TestAccPDNSZoneMetadata_AxfrFrom(t *testing.T) { + resourceName := "powerdns_zone_metadata.test-axfr-from" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPDNSZoneMetadataDestroy, + Steps: []resource.TestStep{ + { + Config: testPDNSZoneMetadata_AxfrFrom, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "kind", "ALLOW-AXFR-FROM"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccPDNSZoneMetadata_AxfrSource(t *testing.T) { + resourceName := "powerdns_zone_metadata.test-axfr-source" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPDNSZoneMetadataDestroy, + Steps: []resource.TestStep{ + { + Config: testPDNSZoneMetadata_AxfrSource, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "kind", "AXFR-SOURCE"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccPDNSZoneMetadata_XTest(t *testing.T) { + resourceName := "powerdns_zone_metadata.test-x-test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPDNSZoneMetadataDestroy, + Steps: []resource.TestStep{ + { + Config: testPDNSZoneMetadata_XTest, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "kind", "X-TEST"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckPDNSZoneMetadataDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { - if rs.Type != "powerdns_zone_metadata" { + for _, resource := range s.RootModule().Resources { + if resource.Type != "powerdns_zone_metadata" { continue } client := testAccProvider.Meta().(*Client) - exists, err := client.RecordExistsByID(rs.Primary.Attributes["zone"], rs.Primary.ID) + exists, err := client.ZoneMetadataExists(resource.Primary.ID) if err != nil { - return fmt.Errorf("Error checking if record still exists: %#v", rs.Primary.ID) + return fmt.Errorf("Error checking if zone metadata still exists: %#v", resource.Primary.ID) } if exists { - return fmt.Errorf("Record still exists: %#v", rs.Primary.ID) + return fmt.Errorf("Zone still exists: %#v", resource.Primary.ID) } } return nil } -func testAccCheckPDNSZoneMetadataExists(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) - } +const testPDNSZoneMetadata_Empty = ` +resource "powerdns_zone_metadata" "empty" { + zone = "sysa.xyz." + kind = "ALLOW-AXFR-FROM" + metadata = [ ] +}` - if rs.Primary.ID == "" { - return fmt.Errorf("No Record ID is set") - } +const testPDNSZoneMetadata_AxfrFrom = ` +resource "powerdns_zone_metadata" "test-axfr-from" { + zone = "sysa.xyz." + kind = "ALLOW-AXFR-FROM" + metadata = ["AUTO-NS"] +}` - 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 testPDNSZoneMetadata_AxfrSource = ` +resource "powerdns_zone_metadata" "test-axfr-source" { + zone = "sysa.xyz." + kind = "AXFR-SOURCE" + metadata = ["10.0.0.1"] +}` -const testPDNSZoneMetadataEmpty = ` -resource "powerdns_zone_metadata" "test-a" { +const testPDNSZoneMetadata_XTest = ` +resource "powerdns_zone_metadata" "test-x-test" { zone = "sysa.xyz." - kind = "ALLOW-AXFR-FROM" - metadata = [ ] + kind = "X-TEST" + metadata = ["test1", "test2"] }` diff --git a/powerdns/resource_powerdns_zone_test.go b/powerdns/resource_powerdns_zone_test.go index 43aafd0d..aac7d37e 100644 --- a/powerdns/resource_powerdns_zone_test.go +++ b/powerdns/resource_powerdns_zone_test.go @@ -11,7 +11,6 @@ import ( func TestAccPDNSZoneNative(t *testing.T) { resourceName := "powerdns_zone.test-native" - t.Skip("skip") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -27,9 +26,10 @@ func TestAccPDNSZoneNative(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"nameservers"}, }, }, }) @@ -83,7 +83,6 @@ func TestAccPDNSZoneNativeSmallCaps(t *testing.T) { func TestAccPDNSZoneMaster(t *testing.T) { resourceName := "powerdns_zone.test-master" - t.Skip("skip") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -99,9 +98,10 @@ func TestAccPDNSZoneMaster(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"nameservers"}, }, }, }) @@ -110,7 +110,6 @@ func TestAccPDNSZoneMaster(t *testing.T) { func TestAccPDNSZoneMasterSOAAPIEDIT(t *testing.T) { resourceName := "powerdns_zone.test-master-soa-edit-api" resourceSOAEDITAPI := `DEFAULT` - t.Skip("skip") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -127,9 +126,10 @@ func TestAccPDNSZoneMasterSOAAPIEDIT(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"nameservers"}, }, }, }) @@ -138,7 +138,6 @@ func TestAccPDNSZoneMasterSOAAPIEDIT(t *testing.T) { func TestAccPDNSZoneMasterSOAAPIEDITEmpty(t *testing.T) { resourceName := "powerdns_zone.test-master-soa-edit-api-empty" resourceSOAEDITAPI := `""` - t.Skip("skip") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -155,9 +154,10 @@ func TestAccPDNSZoneMasterSOAAPIEDITEmpty(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"nameservers"}, }, }, }) @@ -169,7 +169,7 @@ func TestAccPDNSZoneMasterSOAAPIEDITUndefined(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckPDNSZoneDestroy, + CheckDestroy: testAccCheckPDNSZoneDestroy, Steps: []resource.TestStep{ { Config: testPDNSZoneConfigMasterSOAEDITAPIUndefined, @@ -180,9 +180,10 @@ func TestAccPDNSZoneMasterSOAAPIEDITUndefined(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"nameservers"}, }, }, }) @@ -191,7 +192,6 @@ func TestAccPDNSZoneMasterSOAAPIEDITUndefined(t *testing.T) { func TestAccPDNSZoneAccount(t *testing.T) { resourceName := "powerdns_zone.test-account" resourceAccount := `test` - t.Skip("skip") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -208,9 +208,10 @@ func TestAccPDNSZoneAccount(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"nameservers"}, }, }, }) @@ -219,7 +220,6 @@ func TestAccPDNSZoneAccount(t *testing.T) { func TestAccPDNSZoneAccountEmpty(t *testing.T) { resourceName := "powerdns_zone.test-account-empty" resourceAccount := `` - t.Skip("skip") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -236,9 +236,10 @@ func TestAccPDNSZoneAccountEmpty(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"nameservers"}, }, }, }) @@ -247,7 +248,6 @@ func TestAccPDNSZoneAccountEmpty(t *testing.T) { func TestAccPDNSZoneAccountUndefined(t *testing.T) { resourceName := "powerdns_zone.test-account-undefined" resourceAccount := `admin` - t.Skip("skip") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -264,9 +264,10 @@ func TestAccPDNSZoneAccountUndefined(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"nameservers"}, }, }, }) From 1d9a2cf90375c9cb4094dca5ee0ea2d41d20f86d Mon Sep 17 00:00:00 2001 From: Sergey Yakovlev Date: Thu, 20 May 2021 20:42:26 +0300 Subject: [PATCH 6/8] Add documentation --- website/docs/r/metadata.html.markdown | 55 +++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 website/docs/r/metadata.html.markdown diff --git a/website/docs/r/metadata.html.markdown b/website/docs/r/metadata.html.markdown new file mode 100644 index 00000000..5a67cc8d --- /dev/null +++ b/website/docs/r/metadata.html.markdown @@ -0,0 +1,55 @@ +--- +layout: "powerdns" +page_title: "PowerDNS: powerdns_zone_metadata" +sidebar_current: "docs-powerdns-resource-zone-metadata" +description: |- + Provides a PowerDNS zone metadata resource. +--- + +# powerdns\_zone\_metadata + +Provides a PowerDNS zone metadata resource. + +## Example Usage + +All possible zone metadata can be found [here](https://doc.powerdns.com/authoritative/domainmetadata.html#). + +### ALLOW-AXFR-FROM example +For the v1 API (PowerDNS version 4): + +```hcl +# Add ALLOW-AXFR-FROM metadata to the zone +resource "powerdns_zone_metadata" "foobar" { + zone = "example.com." + kind = "ALLOW-AXFR-FROM" + metadata = ["AUTO-NS", "10.0.0.0/24"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `zone` - (Required) The name of zone to contain this metadata. +* `kind` - (Required) The kind of the metadata. +* `metadata` - (Required) A string list of metadata. + +### Attribute Reference + +The id of the resource is a composite of the zone name and metadata kind, joined by a separator - `:::`. + +For example, metadata in zone `foo.test.com.` of kind `ALLOW-AXFR-FROM` will be represented with the following `id`: `foo.test.com.:::ALLOW-AXFR-FROM` + +### Importing + +An existing record can be imported into this resource by supplying both the zone name and metadata kind it belongs to. +If the kind or zone is not found, or if the record is of a different type or in a different zone, an error will be returned. + +For example: + +``` +$ terraform import powerdns_zone_metadata.test-a test.com.:::AXFR-SOURCE +``` + +For more information on how to use terraform's `import` command, please refer to terraform's [core documentation](https://www.terraform.io/docs/import/index.html#currently-state-only). + From 22c8b04b850346163e61a2ccce24ecad5745517b Mon Sep 17 00:00:00 2001 From: Sergei Yakovlev Date: Fri, 4 Jun 2021 19:59:53 +0300 Subject: [PATCH 7/8] Remove ImportStateVerifyIgnore in tests and refactor StatusCode for using constants instead of numbers --- powerdns/client.go | 14 +++---- powerdns/resource_powerdns_zone_test.go | 56 +++++++++++-------------- 2 files changed, 31 insertions(+), 39 deletions(-) diff --git a/powerdns/client.go b/powerdns/client.go index 1aa72283..245203df 100644 --- a/powerdns/client.go +++ b/powerdns/client.go @@ -261,7 +261,7 @@ func (client *Client) detectAPIVersion() (int, error) { } defer resp.Body.Close() - if resp.StatusCode == 200 { + if resp.StatusCode == http.StatusOK { return 1, nil } return 0, nil @@ -303,7 +303,7 @@ func (client *Client) GetZone(name string) (ZoneInfo, error) { } defer resp.Body.Close() - if resp.StatusCode != 200 { + if resp.StatusCode != http.StatusOK { errorResp := new(errorResponse) if err = json.NewDecoder(resp.Body).Decode(errorResp); err != nil { return ZoneInfo{}, fmt.Errorf("Error getting zone: %s", name) @@ -421,7 +421,7 @@ func (client *Client) DeleteZone(name string) error { } defer resp.Body.Close() - if resp.StatusCode != 204 { + if resp.StatusCode != http.StatusNoContent { errorResp := new(errorResponse) if err = json.NewDecoder(resp.Body).Decode(errorResp); err != nil { return fmt.Errorf("Error deleting zone: %s", name) @@ -535,7 +535,7 @@ func (client *Client) ReplaceRecordSet(zone string, rrSet ResourceRecordSet) (st } defer resp.Body.Close() - if resp.StatusCode != 200 && resp.StatusCode != 204 { + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent { errorResp := new(errorResponse) if err = json.NewDecoder(resp.Body).Decode(errorResp); err != nil { return "", fmt.Errorf("Error creating record set: %s", rrSet.ID()) @@ -568,7 +568,7 @@ func (client *Client) DeleteRecordSet(zone string, name string, tpe string) erro } defer resp.Body.Close() - if resp.StatusCode != 200 && resp.StatusCode != 204 { + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent { errorResp := new(errorResponse) if err = json.NewDecoder(resp.Body).Decode(errorResp); err != nil { return fmt.Errorf("Error deleting record: %s %s", name, tpe) @@ -667,7 +667,7 @@ func (client *Client) DeleteZoneMetadata(id string) error { } defer resp.Body.Close() - if resp.StatusCode != 200 { + if resp.StatusCode != http.StatusOK { errorResp := new(errorResponse) if err = json.NewDecoder(resp.Body).Decode(errorResp); err != nil { return fmt.Errorf("Error deleting Zone Metadata: %s", kind) @@ -728,7 +728,7 @@ func (client *Client) setServerVersion() error { return err } defer resp.Body.Close() - if resp.StatusCode != 200 { + if resp.StatusCode != http.StatusOK { return fmt.Errorf("Invalid response code from server: '%d'. Response body: %v", resp.StatusCode, resp.Body) } diff --git a/powerdns/resource_powerdns_zone_test.go b/powerdns/resource_powerdns_zone_test.go index aac7d37e..0e2b4e95 100644 --- a/powerdns/resource_powerdns_zone_test.go +++ b/powerdns/resource_powerdns_zone_test.go @@ -26,10 +26,9 @@ func TestAccPDNSZoneNative(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"nameservers"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -98,10 +97,9 @@ func TestAccPDNSZoneMaster(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"nameservers"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -126,10 +124,9 @@ func TestAccPDNSZoneMasterSOAAPIEDIT(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"nameservers"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -154,10 +151,9 @@ func TestAccPDNSZoneMasterSOAAPIEDITEmpty(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"nameservers"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -180,10 +176,9 @@ func TestAccPDNSZoneMasterSOAAPIEDITUndefined(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"nameservers"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -208,10 +203,9 @@ func TestAccPDNSZoneAccount(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"nameservers"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -236,10 +230,9 @@ func TestAccPDNSZoneAccountEmpty(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"nameservers"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -264,10 +257,9 @@ func TestAccPDNSZoneAccountUndefined(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"nameservers"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) From 8f1266a895e0c3da738d7e82ec2d10fc85d8bc40 Mon Sep 17 00:00:00 2001 From: Sergei Yakovlev Date: Thu, 15 Jul 2021 15:35:29 +0300 Subject: [PATCH 8/8] Fix trim and format string --- powerdns/resource_powerdns_zone_metadata.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/powerdns/resource_powerdns_zone_metadata.go b/powerdns/resource_powerdns_zone_metadata.go index 168f95e4..da01c8c7 100644 --- a/powerdns/resource_powerdns_zone_metadata.go +++ b/powerdns/resource_powerdns_zone_metadata.go @@ -48,8 +48,9 @@ func resourcePDNSZoneMetadataCreate(d *schema.ResourceData, meta interface{}) er mtdata := d.Get("metadata").(*schema.Set).List() for _, mt := range mtdata { - if len(strings.Trim(mt.(string), " ")) == 0 { - log.Printf("[WARN] One or more values in 'metadata' contain empty '' value(s)") + if len(strings.TrimSpace(mt.(string))) == 0 { + log.Printf("[WARN] One or more values in 'metadata' contain empty value(s)") + break } } if !(len(mtdata) > 0) {