From a441b4e3242bb570ac222203d5bf979d69157483 Mon Sep 17 00:00:00 2001 From: Vladimir Danilov Date: Mon, 25 Mar 2024 12:21:24 +0000 Subject: [PATCH] Merge pull request #1169 in CLOUD/terraform-provider-yandex-mirror from CLOUD-63662/dns_records_in_vpc_address to master Squashed commit of the following: commit ceb47157c4ec17c05e1b9a4fe767fe268bf723e8 Author: Vladimir Danilov Date: Mon Mar 25 14:22:16 2024 +0300 [CLOUD-63662] dns record specs in vpc address commit 25f28d027e4842dcb2e59fec654ff4122b35e7a3 Author: Vladimir Danilov Date: Mon Mar 18 14:16:53 2024 +0300 [CLOUD-63662] dns record specs in vpc address --- CHANGELOG.md | 1 + website/docs/r/vpc_address.html.markdown | 12 ++++ yandex/data_source_yandex_vpc_address.go | 24 +++++++ yandex/resource_yandex_vpc_address.go | 82 +++++++++++++++++++--- yandex/resource_yandex_vpc_address_test.go | 15 ++++ yandex/structures.go | 45 ++++++++++++ 6 files changed, 169 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 587be81d4..03550effb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ ENHANCEMENTS: * compute: change `secondary_disk` attribute type from List to Set in `instance` resource. * greenplum: fixed `pxf_config` fetching in yandex_mdb_greenplum_cluster datasource. * vpc: allow to change cidrs in subnet (v4_cidr_blocks) +* vpc: add `dns_record` attribute in `yandex_vpc_address` resource and data source ## 0.112.0 (March 12, 2024) BUG FIXES: diff --git a/website/docs/r/vpc_address.html.markdown b/website/docs/r/vpc_address.html.markdown index f59b385ab..ebf091067 100644 --- a/website/docs/r/vpc_address.html.markdown +++ b/website/docs/r/vpc_address.html.markdown @@ -68,6 +68,18 @@ The `external_ipv4_address` block supports: ~> **NOTE:** Either one `ddos_protection_provider` or `outgoing_smtp_capability` arguments can be specified. ~> **NOTE:** Change any argument in `external_ipv4_address` will cause an address recreate +--- + +* `dns_record` - (Optional) DNS record specification of address +--- + +The `dns_record` block supports: + +* `fqdn` - (Required) FQDN for record to address +* `dns_zone_id` - (Optional) DNS zone id to create record at. When not set, private zone used. +* `ttl` - (Optional) TTL of DNS record +* `ptr` - (Optional) If PTR record is needed + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are exported: diff --git a/yandex/data_source_yandex_vpc_address.go b/yandex/data_source_yandex_vpc_address.go index 56119ae7d..71dab8c2d 100644 --- a/yandex/data_source_yandex_vpc_address.go +++ b/yandex/data_source_yandex_vpc_address.go @@ -74,6 +74,30 @@ func dataSourceYandexVPCAddress() *schema.Resource { Type: schema.TypeBool, Computed: true, }, + "dns_record": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dns_zone_id": { + Type: schema.TypeString, + Computed: true, + }, + "fqdn": { + Type: schema.TypeString, + Computed: true, + }, + "ttl": { + Type: schema.TypeInt, + Computed: true, + }, + "ptr": { + Type: schema.TypeBool, + Computed: true, + }, + }, + }, + }, }, } } diff --git a/yandex/resource_yandex_vpc_address.go b/yandex/resource_yandex_vpc_address.go index 7695ac0f1..484c0d97e 100644 --- a/yandex/resource_yandex_vpc_address.go +++ b/yandex/resource_yandex_vpc_address.go @@ -3,6 +3,7 @@ package yandex import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -22,10 +23,10 @@ func handleAddressNotFoundError(err error, d *schema.ResourceData, id string) er func resourceYandexVPCAddress() *schema.Resource { return &schema.Resource{ - Read: resourceYandexVPCAddressRead, - Create: resourceYandexVPCAddressCreate, - Update: resourceYandexVPCAddressUpdate, - Delete: resourceYandexVPCAddressDelete, + Read: resourceYandexVPCAddressRead, + Create: resourceYandexVPCAddressCreate, + UpdateContext: resourceYandexVPCAddressUpdateContext, + Delete: resourceYandexVPCAddressDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, @@ -111,6 +112,30 @@ func resourceYandexVPCAddress() *schema.Resource { Optional: true, Computed: true, }, + "dns_record": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dns_zone_id": { + Type: schema.TypeString, + Computed: true, + }, + "fqdn": { + Type: schema.TypeString, + Computed: true, + }, + "ttl": { + Type: schema.TypeInt, + Computed: true, + }, + "ptr": { + Type: schema.TypeBool, + Computed: true, + }, + }, + }, + }, }, } } @@ -155,6 +180,12 @@ func yandexVPCAddressRead(d *schema.ResourceData, meta interface{}, id string) e if err := d.Set("reserved", address.GetReserved()); err != nil { return err } + + dnsRecords := flattenVpcAddressDnsRecords(address.DnsRecords) + if err := d.Set("dns_record", dnsRecords); err != nil { + return err + } + return d.Set("used", address.GetUsed()) } @@ -180,6 +211,11 @@ func resourceYandexVPCAddressCreate(d *schema.ResourceData, meta interface{}) er return addressError("expanding external ipv4 address while creating address: %s", err) } + dnsSpecs, err := expandVpcAddressDnsRecords(d) + if err != nil { + return addressError("expanding dns record specs while creating address %s", err) + } + req := vpc.CreateAddressRequest{ FolderId: folderID, Name: d.Get("name").(string), @@ -190,6 +226,7 @@ func resourceYandexVPCAddressCreate(d *schema.ResourceData, meta interface{}) er ExternalIpv4AddressSpec: spec, }, DeletionProtection: d.Get("deletion_protection").(bool), + DnsRecordSpecs: dnsSpecs, } ctx, cancel := context.WithTimeout(config.Context(), d.Timeout(schema.TimeoutCreate)) @@ -224,7 +261,7 @@ func resourceYandexVPCAddressCreate(d *schema.ResourceData, meta interface{}) er return resourceYandexVPCAddressRead(d, meta) } -func resourceYandexVPCAddressUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceYandexVPCAddressUpdateContext(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { config := meta.(*Config) d.Partial(true) @@ -238,7 +275,7 @@ func resourceYandexVPCAddressUpdate(d *schema.ResourceData, meta interface{}) er if d.HasChange(addrLabelsPropName) { labelsProp, err := expandLabels(d.Get(addrLabelsPropName)) if err != nil { - return err + return diag.FromErr(err) } req.Labels = labelsProp @@ -263,22 +300,47 @@ func resourceYandexVPCAddressUpdate(d *schema.ResourceData, meta interface{}) er req.UpdateMask.Paths = append(req.UpdateMask.Paths, addrDeletionProtectionPropName) } - ctx, cancel := context.WithTimeout(config.Context(), d.Timeout(schema.TimeoutUpdate)) + const addrDnsRecords = "dns_record" + if d.HasChange(addrDnsRecords) { + specs, err := expandVpcAddressDnsRecords(d) + if err != nil { + return diag.FromErr(err) + } + req.DnsRecordSpecs = specs + // differs in ycp and tf + req.UpdateMask.Paths = append(req.UpdateMask.Paths, "dns_record_specs") + } + + var diags diag.Diagnostics + if d.HasChange("reserved") && !req.Reserved && len(req.DnsRecordSpecs) > 0 { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "DNS records were copied to the network interface", + Detail: "You changed the type of address to ephemeral. This copies DNS records to the network interface. " + + "Don't forget to update it in Terraform specification!", + }) + } + + ctx, cancel := context.WithTimeout(ctx, d.Timeout(schema.TimeoutUpdate)) defer cancel() op, err := config.sdk.WrapOperation(config.sdk.VPC().Address().Update(ctx, req)) if err != nil { - return addressError("while requesting API to update Address %q: %s", d.Id(), err) + return diag.FromErr(addressError("while requesting API to update Address %q: %s", d.Id(), err)) } err = op.Wait(ctx) if err != nil { - return addressError("updating Address %q: %s", d.Id(), err) + return diag.FromErr(addressError("updating Address %q: %s", d.Id(), err)) } d.Partial(false) - return resourceYandexVPCAddressRead(d, meta) + err = resourceYandexVPCAddressRead(d, meta) + if err != nil { + return diag.FromErr(err) + } + return diags } func resourceYandexVPCAddressDelete(d *schema.ResourceData, meta interface{}) error { diff --git a/yandex/resource_yandex_vpc_address_test.go b/yandex/resource_yandex_vpc_address_test.go index 611ebf8ff..d9454530b 100644 --- a/yandex/resource_yandex_vpc_address_test.go +++ b/yandex/resource_yandex_vpc_address_test.go @@ -27,6 +27,10 @@ resource "yandex_vpc_address" "addr1" { ddos_protection_provider = "qrator" } deletion_protection = true + + dns_record { + fqdn = "some.fqdn." + } } `, name) } @@ -44,6 +48,11 @@ resource "yandex_vpc_address" "addr1" { external_ipv4_address { zone_id = "ru-central1-c" } + + dns_record { + fqdn = "other.fqdn." + ptr = true + } } `, name) } @@ -99,6 +108,9 @@ func TestAccVPCAddress_basic(t *testing.T) { testAccCheckVPCAddressContainsLabel(&address, "tf-label", "tf-label-value"), testAccCheckVPCAddressContainsLabel(&address, "empty-label", ""), testAccCheckCreatedAtAttr("yandex_vpc_address.addr1"), + resource.TestCheckResourceAttr("yandex_vpc_address.addr1", "dns_record.#", "1"), + resource.TestCheckResourceAttr("yandex_vpc_address.addr1", "dns_record.0.fqdn", "some.fqdn"), + resource.TestCheckResourceAttr("yandex_vpc_address.addr1", "dns_record.0.ptr", "true"), ), }, { @@ -119,6 +131,8 @@ func TestAccVPCAddress_basic(t *testing.T) { testAccCheckVPCAddressContainsLabelNotFound(&address, "empty-label"), testAccCheckVPCAddressContainsLabel(&address, "new-label", "new"), testAccCheckCreatedAtAttr("yandex_vpc_address.addr1"), + resource.TestCheckResourceAttr("yandex_vpc_address.addr1", "dns_record.#", "1"), + resource.TestCheckResourceAttr("yandex_vpc_address.addr1", "dns_record.0.fqdn", "other.fqdn"), ), }, { @@ -132,6 +146,7 @@ func TestAccVPCAddress_basic(t *testing.T) { "yandex_vpc_address.addr1", "external_ipv4_address.0.ddos_protection_provider", "qrator", ), resource.TestCheckResourceAttr("yandex_vpc_address.addr1", "deletion_protection", "false"), + resource.TestCheckResourceAttr("yandex_vpc_address.addr1", "dns_record.#", "0"), ), }, { diff --git a/yandex/structures.go b/yandex/structures.go index bfb8c627f..6bd3ba227 100644 --- a/yandex/structures.go +++ b/yandex/structures.go @@ -1695,6 +1695,21 @@ func flattenSecurityGroupRulesSpec(sg []*vpc.SecurityGroupRule) (*schema.Set, *s return ingress, egress } +func flattenVpcAddressDnsRecords(specs []*vpc.DnsRecord) []map[string]interface{} { + res := make([]map[string]interface{}, len(specs)) + + for i, spec := range specs { + res[i] = map[string]interface{}{ + "fqdn": spec.Fqdn, + "dns_zone_id": spec.DnsZoneId, + "ttl": int(spec.Ttl), + "ptr": spec.Ptr, + } + } + + return res +} + func flattenExternalIpV4AddressSpec(address *vpc.ExternalIpv4Address) []interface{} { if address == nil { return nil @@ -1743,6 +1758,36 @@ func expandAddressRequirements(addrDesc map[string]interface{}) (*vpc.AddressReq return &requirements, set } +func expandVpcAddressDnsRecords(d *schema.ResourceData) ([]*vpc.DnsRecordSpec, error) { + var ( + v interface{} + ok bool + ) + + if v, ok = d.GetOk("dns_record"); !ok { + return nil, nil + } + + specs := v.([]interface{}) + recs := make([]*vpc.DnsRecordSpec, len(specs)) + + for i, raw := range specs { + d := raw.(map[string]interface{}) + r := &vpc.DnsRecordSpec{Fqdn: d["fqdn"].(string)} + if s, ok := d["dns_zone_id"]; ok { + r.DnsZoneId = s.(string) + } + if s, ok := d["ttl"]; ok { + r.Ttl = int64(s.(int)) + } + if s, ok := d["ptr"]; ok { + r.Ptr = s.(bool) + } + recs[i] = r + } + return recs, nil +} + func expandExternalIpv4Address(d *schema.ResourceData) (*vpc.ExternalIpv4AddressSpec, error) { var ( v interface{}