diff --git a/google/provider.go b/google/provider.go index 2a51a49d308..76ff1e1d36e 100644 --- a/google/provider.go +++ b/google/provider.go @@ -414,9 +414,9 @@ func Provider() terraform.ResourceProvider { return provider } -// Generated resources: 78 +// Generated resources: 79 // Generated IAM resources: 18 -// Total generated resources: 96 +// Total generated resources: 97 func ResourceMap() map[string]*schema.Resource { resourceMap, _ := ResourceMapWithErrors() return resourceMap @@ -464,6 +464,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_compute_region_disk": resourceComputeRegionDisk(), "google_compute_route": resourceComputeRoute(), "google_compute_router": resourceComputeRouter(), + "google_compute_router_nat": resourceComputeRouterNat(), "google_compute_snapshot": resourceComputeSnapshot(), "google_compute_ssl_certificate": resourceComputeSslCertificate(), "google_compute_ssl_policy": resourceComputeSslPolicy(), @@ -547,7 +548,6 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_compute_project_metadata_item": resourceComputeProjectMetadataItem(), "google_compute_region_instance_group_manager": resourceComputeRegionInstanceGroupManager(), "google_compute_router_interface": resourceComputeRouterInterface(), - "google_compute_router_nat": resourceComputeRouterNat(), "google_compute_router_peer": resourceComputeRouterPeer(), "google_compute_security_policy": resourceComputeSecurityPolicy(), "google_compute_shared_vpc_host_project": resourceComputeSharedVpcHostProject(), diff --git a/google/resource_compute_backend_bucket_signed_url_key.go b/google/resource_compute_backend_bucket_signed_url_key.go index b0ee243819f..ec6632bbc00 100644 --- a/google/resource_compute_backend_bucket_signed_url_key.go +++ b/google/resource_compute_backend_bucket_signed_url_key.go @@ -268,14 +268,21 @@ func flattenNestedComputeBackendBucketSignedUrlKey(d *schema.ResourceData, meta return nil, fmt.Errorf("expected list or map for value cdnPolicy.signedUrlKeyNames. Actual value: %v", v) } - expectedName, err := expandComputeBackendBucketSignedUrlKeyName(d.Get("name"), d, meta.(*Config)) + _, item, err := resourceComputeBackendBucketSignedUrlKeyFindNestedObjectInList(d, meta, v.([]interface{})) if err != nil { return nil, err } + return item, nil +} + +func resourceComputeBackendBucketSignedUrlKeyFindNestedObjectInList(d *schema.ResourceData, meta interface{}, items []interface{}) (index int, item map[string]interface{}, err error) { + expectedName, err := expandComputeBackendBucketSignedUrlKeyName(d.Get("name"), d, meta.(*Config)) + if err != nil { + return -1, nil, err + } // Search list for this resource. - items := v.([]interface{}) - for _, itemRaw := range items { + for idx, itemRaw := range items { if itemRaw == nil { continue } @@ -290,8 +297,7 @@ func flattenNestedComputeBackendBucketSignedUrlKey(d *schema.ResourceData, meta continue } log.Printf("[DEBUG] Found item for resource %q: %#v)", d.Id(), item) - return item, nil + return idx, item, nil } - - return nil, nil + return -1, nil, nil } diff --git a/google/resource_compute_backend_service_signed_url_key.go b/google/resource_compute_backend_service_signed_url_key.go index f4120433763..37e48ce0adf 100644 --- a/google/resource_compute_backend_service_signed_url_key.go +++ b/google/resource_compute_backend_service_signed_url_key.go @@ -268,14 +268,21 @@ func flattenNestedComputeBackendServiceSignedUrlKey(d *schema.ResourceData, meta return nil, fmt.Errorf("expected list or map for value cdnPolicy.signedUrlKeyNames. Actual value: %v", v) } - expectedName, err := expandComputeBackendServiceSignedUrlKeyName(d.Get("name"), d, meta.(*Config)) + _, item, err := resourceComputeBackendServiceSignedUrlKeyFindNestedObjectInList(d, meta, v.([]interface{})) if err != nil { return nil, err } + return item, nil +} + +func resourceComputeBackendServiceSignedUrlKeyFindNestedObjectInList(d *schema.ResourceData, meta interface{}, items []interface{}) (index int, item map[string]interface{}, err error) { + expectedName, err := expandComputeBackendServiceSignedUrlKeyName(d.Get("name"), d, meta.(*Config)) + if err != nil { + return -1, nil, err + } // Search list for this resource. - items := v.([]interface{}) - for _, itemRaw := range items { + for idx, itemRaw := range items { if itemRaw == nil { continue } @@ -290,8 +297,7 @@ func flattenNestedComputeBackendServiceSignedUrlKey(d *schema.ResourceData, meta continue } log.Printf("[DEBUG] Found item for resource %q: %#v)", d.Id(), item) - return item, nil + return idx, item, nil } - - return nil, nil + return -1, nil, nil } diff --git a/google/resource_compute_network_endpoint.go b/google/resource_compute_network_endpoint.go index 5e7189cdb95..26f32105705 100644 --- a/google/resource_compute_network_endpoint.go +++ b/google/resource_compute_network_endpoint.go @@ -361,22 +361,29 @@ func flattenNestedComputeNetworkEndpoint(d *schema.ResourceData, meta interface{ return nil, fmt.Errorf("expected list or map for value items. Actual value: %v", v) } - expectedInstance, err := expandComputeNetworkEndpointInstance(d.Get("instance"), d, meta.(*Config)) + _, item, err := resourceComputeNetworkEndpointFindNestedObjectInList(d, meta, v.([]interface{})) if err != nil { return nil, err } + return item, nil +} + +func resourceComputeNetworkEndpointFindNestedObjectInList(d *schema.ResourceData, meta interface{}, items []interface{}) (index int, item map[string]interface{}, err error) { + expectedInstance, err := expandComputeNetworkEndpointInstance(d.Get("instance"), d, meta.(*Config)) + if err != nil { + return -1, nil, err + } expectedIpAddress, err := expandComputeNetworkEndpointIpAddress(d.Get("ip_address"), d, meta.(*Config)) if err != nil { - return nil, err + return -1, nil, err } expectedPort, err := expandComputeNetworkEndpointPort(d.Get("port"), d, meta.(*Config)) if err != nil { - return nil, err + return -1, nil, err } // Search list for this resource. - items := v.([]interface{}) - for _, itemRaw := range items { + for idx, itemRaw := range items { if itemRaw == nil { continue } @@ -385,7 +392,7 @@ func flattenNestedComputeNetworkEndpoint(d *schema.ResourceData, meta interface{ // Decode list item before comparing. item, err := resourceComputeNetworkEndpointDecoder(d, meta, item) if err != nil { - return nil, err + return -1, nil, err } itemInstance := flattenComputeNetworkEndpointInstance(item["instance"], d) @@ -404,12 +411,10 @@ func flattenNestedComputeNetworkEndpoint(d *schema.ResourceData, meta interface{ continue } log.Printf("[DEBUG] Found item for resource %q: %#v)", d.Id(), item) - return item, nil + return idx, item, nil } - - return nil, nil + return -1, nil, nil } - func resourceComputeNetworkEndpointDecoder(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) { v, ok := res["networkEndpoint"] if !ok || v == nil { diff --git a/google/resource_compute_router_nat.go b/google/resource_compute_router_nat.go index f81e4d4bd7d..231bf7517ba 100644 --- a/google/resource_compute_router_nat.go +++ b/google/resource_compute_router_nat.go @@ -1,14 +1,29 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + package google import ( "fmt" "log" + "reflect" + "strconv" "time" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" - computeBeta "google.golang.org/api/compute/v0.beta" - + "google.golang.org/api/compute/v1" "google.golang.org/api/googleapi" ) @@ -18,12 +33,14 @@ func resourceComputeRouterNat() *schema.Resource { Read: resourceComputeRouterNatRead, Update: resourceComputeRouterNatUpdate, Delete: resourceComputeRouterNatDelete, + Importer: &schema.ResourceImporter{ State: resourceComputeRouterNatImport, }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), Delete: schema.DefaultTimeout(10 * time.Minute), }, @@ -34,49 +51,41 @@ func resourceComputeRouterNat() *schema.Resource { ForceNew: true, ValidateFunc: validateRFC1035Name(2, 63), }, - "router": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, "nat_ip_allocate_option": { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{"MANUAL_ONLY", "AUTO_ONLY"}, false), }, - "nat_ips": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, + "router": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, }, "source_subnetwork_ip_ranges_to_nat": { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{"ALL_SUBNETWORKS_ALL_IP_RANGES", "ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES", "LIST_OF_SUBNETWORKS"}, false), }, - "subnetwork": { - Type: schema.TypeSet, + "icmp_idle_timeout_sec": { + Type: schema.TypeInt, + Optional: true, + Default: 30, + }, + "log_config": { + Type: schema.TypeList, Optional: true, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - }, - - // this field is optional with a default in the API, but we - // don't have the ability to support complex defaults inside - // nested fields - "source_ip_ranges_to_nat": { - Type: schema.TypeSet, + "enable": { + Type: schema.TypeBool, Required: true, - MinItems: 1, - Elem: &schema.Schema{Type: schema.TypeString}, }, - "secondary_ip_range_names": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, + "filter": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"ERRORS_ONLY", "TRANSLATIONS_ONLY", "ALL"}, false), }, }, }, @@ -85,39 +94,42 @@ func resourceComputeRouterNat() *schema.Resource { Type: schema.TypeInt, Optional: true, }, - "udp_idle_timeout_sec": { - Type: schema.TypeInt, + "nat_ips": { + Type: schema.TypeSet, Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + DiffSuppressFunc: compareSelfLinkOrResourceName, + }, + // Default schema.HashSchema is used. }, - "icmp_idle_timeout_sec": { - Type: schema.TypeInt, + "region": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + }, + "subnetwork": { + Type: schema.TypeSet, Optional: true, + Elem: computeRouterNatSubnetworkSchema(), + // Default schema.HashSchema is used. }, "tcp_established_idle_timeout_sec": { Type: schema.TypeInt, Optional: true, + Default: 1200, }, "tcp_transitory_idle_timeout_sec": { Type: schema.TypeInt, Optional: true, + Default: 30, }, - "log_config": { - Type: schema.TypeList, + "udp_idle_timeout_sec": { + Type: schema.TypeInt, Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "enable": { - Type: schema.TypeBool, - Required: true, - }, - "filter": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{"ERRORS_ONLY", "TRANSLATIONS_ONLY", "ALL"}, false), - }, - }, - }, + Default: 30, }, "project": { Type: schema.TypeString, @@ -125,11 +137,34 @@ func resourceComputeRouterNat() *schema.Resource { Computed: true, ForceNew: true, }, - "region": { - Type: schema.TypeString, + }, + } +} + +func computeRouterNatSubnetworkSchema() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + }, + "source_ip_ranges_to_nat": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Set: schema.HashString, + }, + "secondary_ip_range_names": { + Type: schema.TypeSet, Optional: true, - Computed: true, - ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Set: schema.HashString, }, }, } @@ -138,85 +173,133 @@ func resourceComputeRouterNat() *schema.Resource { func resourceComputeRouterNatCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - region, err := getRegion(d, config) + obj := make(map[string]interface{}) + nameProp, err := expandComputeRouterNatName(d.Get("name"), d, config) if err != nil { return err + } else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) { + obj["name"] = nameProp } - - project, err := getProject(d, config) + natIpAllocateOptionProp, err := expandComputeRouterNatNatIpAllocateOption(d.Get("nat_ip_allocate_option"), d, config) if err != nil { return err + } else if v, ok := d.GetOkExists("nat_ip_allocate_option"); !isEmptyValue(reflect.ValueOf(natIpAllocateOptionProp)) && (ok || !reflect.DeepEqual(v, natIpAllocateOptionProp)) { + obj["natIpAllocateOption"] = natIpAllocateOptionProp } - - routerName := d.Get("router").(string) - natName := d.Get("name").(string) - - routerLock := getRouterLockName(region, routerName) - mutexKV.Lock(routerLock) - defer mutexKV.Unlock(routerLock) - - routersService := config.clientComputeBeta.Routers - router, err := routersService.Get(project, region, routerName).Do() + natIpsProp, err := expandComputeRouterNatNatIps(d.Get("nat_ips"), d, config) if err != nil { - if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { - return fmt.Errorf("Router %s/%s not found", region, routerName) - } - - return fmt.Errorf("Error Reading router %s/%s: %s", region, routerName, err) + return err + } else if v, ok := d.GetOkExists("nat_ips"); ok || !reflect.DeepEqual(v, natIpsProp) { + obj["natIps"] = natIpsProp } - - nats := router.Nats - for _, nat := range nats { - if nat.Name == natName { - return fmt.Errorf("Router %s has nat %s already", routerName, natName) - } + sourceSubnetworkIpRangesToNatProp, err := expandComputeRouterNatSourceSubnetworkIpRangesToNat(d.Get("source_subnetwork_ip_ranges_to_nat"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("source_subnetwork_ip_ranges_to_nat"); !isEmptyValue(reflect.ValueOf(sourceSubnetworkIpRangesToNatProp)) && (ok || !reflect.DeepEqual(v, sourceSubnetworkIpRangesToNatProp)) { + obj["sourceSubnetworkIpRangesToNat"] = sourceSubnetworkIpRangesToNatProp } - - nat := &computeBeta.RouterNat{ - Name: natName, - NatIpAllocateOption: d.Get("nat_ip_allocate_option").(string), - NatIps: convertStringArr(d.Get("nat_ips").(*schema.Set).List()), - SourceSubnetworkIpRangesToNat: d.Get("source_subnetwork_ip_ranges_to_nat").(string), - MinPortsPerVm: int64(d.Get("min_ports_per_vm").(int)), - UdpIdleTimeoutSec: int64(d.Get("udp_idle_timeout_sec").(int)), - IcmpIdleTimeoutSec: int64(d.Get("icmp_idle_timeout_sec").(int)), - TcpEstablishedIdleTimeoutSec: int64(d.Get("tcp_established_idle_timeout_sec").(int)), - TcpTransitoryIdleTimeoutSec: int64(d.Get("tcp_transitory_idle_timeout_sec").(int)), + subnetworksProp, err := expandComputeRouterNatSubnetwork(d.Get("subnetwork"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("subnetwork"); ok || !reflect.DeepEqual(v, subnetworksProp) { + obj["subnetworks"] = subnetworksProp + } + minPortsPerVmProp, err := expandComputeRouterNatMinPortsPerVm(d.Get("min_ports_per_vm"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("min_ports_per_vm"); !isEmptyValue(reflect.ValueOf(minPortsPerVmProp)) && (ok || !reflect.DeepEqual(v, minPortsPerVmProp)) { + obj["minPortsPerVm"] = minPortsPerVmProp + } + udpIdleTimeoutSecProp, err := expandComputeRouterNatUdpIdleTimeoutSec(d.Get("udp_idle_timeout_sec"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("udp_idle_timeout_sec"); !isEmptyValue(reflect.ValueOf(udpIdleTimeoutSecProp)) && (ok || !reflect.DeepEqual(v, udpIdleTimeoutSecProp)) { + obj["udpIdleTimeoutSec"] = udpIdleTimeoutSecProp + } + icmpIdleTimeoutSecProp, err := expandComputeRouterNatIcmpIdleTimeoutSec(d.Get("icmp_idle_timeout_sec"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("icmp_idle_timeout_sec"); !isEmptyValue(reflect.ValueOf(icmpIdleTimeoutSecProp)) && (ok || !reflect.DeepEqual(v, icmpIdleTimeoutSecProp)) { + obj["icmpIdleTimeoutSec"] = icmpIdleTimeoutSecProp + } + tcpEstablishedIdleTimeoutSecProp, err := expandComputeRouterNatTcpEstablishedIdleTimeoutSec(d.Get("tcp_established_idle_timeout_sec"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("tcp_established_idle_timeout_sec"); !isEmptyValue(reflect.ValueOf(tcpEstablishedIdleTimeoutSecProp)) && (ok || !reflect.DeepEqual(v, tcpEstablishedIdleTimeoutSecProp)) { + obj["tcpEstablishedIdleTimeoutSec"] = tcpEstablishedIdleTimeoutSecProp + } + tcpTransitoryIdleTimeoutSecProp, err := expandComputeRouterNatTcpTransitoryIdleTimeoutSec(d.Get("tcp_transitory_idle_timeout_sec"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("tcp_transitory_idle_timeout_sec"); !isEmptyValue(reflect.ValueOf(tcpTransitoryIdleTimeoutSecProp)) && (ok || !reflect.DeepEqual(v, tcpTransitoryIdleTimeoutSecProp)) { + obj["tcpTransitoryIdleTimeoutSec"] = tcpTransitoryIdleTimeoutSecProp + } + logConfigProp, err := expandComputeRouterNatLogConfig(d.Get("log_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("log_config"); !isEmptyValue(reflect.ValueOf(logConfigProp)) && (ok || !reflect.DeepEqual(v, logConfigProp)) { + obj["logConfig"] = logConfigProp } - if v, ok := d.GetOk("subnetwork"); ok { - nat.Subnetworks = expandSubnetworks(v.(*schema.Set).List()) + lockName, err := replaceVars(d, config, "router/{{region}}/{{router}}") + if err != nil { + return err } + mutexKV.Lock(lockName) + defer mutexKV.Unlock(lockName) - if v, ok := d.GetOk("log_config"); ok { - nat.LogConfig = expandLogConfig(v) + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/routers/{{router}}") + if err != nil { + return err } - log.Printf("[INFO] Adding nat %s", natName) - nats = append(nats, nat) - patchRouter := &computeBeta.Router{ - Nats: nats, + log.Printf("[DEBUG] Creating new RouterNat: %#v", obj) + + obj, err = resourceComputeRouterNatPatchCreateEncoder(d, meta, obj) + if err != nil { + return err + } + project, err := getProject(d, config) + if err != nil { + return err + } + res, err := sendRequestWithTimeout(config, "PATCH", project, url, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating RouterNat: %s", err) } - log.Printf("[DEBUG] Updating router %s/%s with nats: %+v", region, routerName, nats) - op, err := routersService.Patch(project, region, router.Name, patchRouter).Do() + // Store the ID now + id, err := replaceVars(d, config, "{{project}}/{{region}}/{{router}}/{{name}}") if err != nil { - return fmt.Errorf("Error patching router %s/%s: %s", region, routerName, err) + return fmt.Errorf("Error constructing id: %s", err) } - d.SetId(fmt.Sprintf("%s/%s/%s/%s", project, region, routerName, natName)) - err = computeBetaOperationWaitTime(config.clientCompute, op, project, "Patching router", int(d.Timeout(schema.TimeoutCreate).Minutes())) + d.SetId(id) + + op := &compute.Operation{} + err = Convert(res, op) if err != nil { + return err + } + + waitErr := computeOperationWaitTime( + config.clientCompute, op, project, "Creating RouterNat", + int(d.Timeout(schema.TimeoutCreate).Minutes())) + + if waitErr != nil { + // The resource didn't actually create d.SetId("") - return fmt.Errorf("Error waiting to patch router %s/%s: %s", region, routerName, err) + return fmt.Errorf("Error waiting to create RouterNat: %s", waitErr) } + log.Printf("[DEBUG] Finished creating RouterNat %q: %#v", d.Id(), res) + return resourceComputeRouterNatRead(d, meta) } func resourceComputeRouterNatRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - region, err := getRegion(d, config) + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/routers/{{router}}") if err != nil { return err } @@ -225,146 +308,170 @@ func resourceComputeRouterNatRead(d *schema.ResourceData, meta interface{}) erro if err != nil { return err } - - routerName := d.Get("router").(string) - natName := d.Get("name").(string) - - routersService := config.clientComputeBeta.Routers - router, err := routersService.Get(project, region, routerName).Do() + res, err := sendRequest(config, "GET", project, url, nil) if err != nil { - if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { - log.Printf("[WARN] Removing router nat %s because its router %s/%s is gone", natName, region, routerName) - d.SetId("") - - return nil - } - - return fmt.Errorf("Error Reading router %s/%s: %s", region, routerName, err) + return handleNotFoundError(err, d, fmt.Sprintf("ComputeRouterNat %q", d.Id())) } - for _, nat := range router.Nats { - if nat.Name == natName { - d.SetId(fmt.Sprintf("%s/%s/%s/%s", project, region, routerName, natName)) - d.Set("nat_ip_allocate_option", nat.NatIpAllocateOption) - d.Set("nat_ips", schema.NewSet(schema.HashString, convertStringArrToInterface(convertSelfLinksToV1(nat.NatIps)))) - d.Set("source_subnetwork_ip_ranges_to_nat", nat.SourceSubnetworkIpRangesToNat) - d.Set("min_ports_per_vm", nat.MinPortsPerVm) - d.Set("udp_idle_timeout_sec", nat.UdpIdleTimeoutSec) - d.Set("icmp_idle_timeout_sec", nat.IcmpIdleTimeoutSec) - d.Set("tcp_established_idle_timeout_sec", nat.TcpEstablishedIdleTimeoutSec) - d.Set("tcp_transitory_idle_timeout_sec", nat.TcpTransitoryIdleTimeoutSec) - d.Set("region", region) - d.Set("project", project) + res, err = flattenNestedComputeRouterNat(d, meta, res) + if err != nil { + return err + } - if err := d.Set("subnetwork", flattenRouterNatSubnetworkToNatBeta(nat.Subnetworks)); err != nil { - return fmt.Errorf("Error reading router nat: %s", err) - } + if res == nil { + // Object isn't there any more - remove it from the state. + log.Printf("[DEBUG] Removing ComputeRouterNat because it couldn't be matched.") + d.SetId("") + return nil + } - if err := d.Set("log_config", flattenRouterNatLogConfig(nat.LogConfig)); err != nil { - return fmt.Errorf("Error reading router nat: %s", err) - } + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading RouterNat: %s", err) + } - return nil - } + if err := d.Set("name", flattenComputeRouterNatName(res["name"], d)); err != nil { + return fmt.Errorf("Error reading RouterNat: %s", err) + } + if err := d.Set("nat_ip_allocate_option", flattenComputeRouterNatNatIpAllocateOption(res["natIpAllocateOption"], d)); err != nil { + return fmt.Errorf("Error reading RouterNat: %s", err) + } + if err := d.Set("nat_ips", flattenComputeRouterNatNatIps(res["natIps"], d)); err != nil { + return fmt.Errorf("Error reading RouterNat: %s", err) + } + if err := d.Set("source_subnetwork_ip_ranges_to_nat", flattenComputeRouterNatSourceSubnetworkIpRangesToNat(res["sourceSubnetworkIpRangesToNat"], d)); err != nil { + return fmt.Errorf("Error reading RouterNat: %s", err) + } + if err := d.Set("subnetwork", flattenComputeRouterNatSubnetwork(res["subnetworks"], d)); err != nil { + return fmt.Errorf("Error reading RouterNat: %s", err) + } + if err := d.Set("min_ports_per_vm", flattenComputeRouterNatMinPortsPerVm(res["minPortsPerVm"], d)); err != nil { + return fmt.Errorf("Error reading RouterNat: %s", err) + } + if err := d.Set("udp_idle_timeout_sec", flattenComputeRouterNatUdpIdleTimeoutSec(res["udpIdleTimeoutSec"], d)); err != nil { + return fmt.Errorf("Error reading RouterNat: %s", err) + } + if err := d.Set("icmp_idle_timeout_sec", flattenComputeRouterNatIcmpIdleTimeoutSec(res["icmpIdleTimeoutSec"], d)); err != nil { + return fmt.Errorf("Error reading RouterNat: %s", err) + } + if err := d.Set("tcp_established_idle_timeout_sec", flattenComputeRouterNatTcpEstablishedIdleTimeoutSec(res["tcpEstablishedIdleTimeoutSec"], d)); err != nil { + return fmt.Errorf("Error reading RouterNat: %s", err) + } + if err := d.Set("tcp_transitory_idle_timeout_sec", flattenComputeRouterNatTcpTransitoryIdleTimeoutSec(res["tcpTransitoryIdleTimeoutSec"], d)); err != nil { + return fmt.Errorf("Error reading RouterNat: %s", err) + } + if err := d.Set("log_config", flattenComputeRouterNatLogConfig(res["logConfig"], d)); err != nil { + return fmt.Errorf("Error reading RouterNat: %s", err) } - log.Printf("[WARN] Removing router nat %s/%s/%s because it is gone", region, routerName, natName) - d.SetId("") return nil } func resourceComputeRouterNatUpdate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - region, err := getRegion(d, config) + project, err := getProject(d, config) if err != nil { return err } - project, err := getProject(d, config) + obj := make(map[string]interface{}) + natIpAllocateOptionProp, err := expandComputeRouterNatNatIpAllocateOption(d.Get("nat_ip_allocate_option"), d, config) if err != nil { return err + } else if v, ok := d.GetOkExists("nat_ip_allocate_option"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, natIpAllocateOptionProp)) { + obj["natIpAllocateOption"] = natIpAllocateOptionProp } - - routerName := d.Get("router").(string) - natName := d.Get("name").(string) - - routerLock := getRouterLockName(region, routerName) - mutexKV.Lock(routerLock) - defer mutexKV.Unlock(routerLock) - - // Get router - router, err := config.clientComputeBeta.Routers.Get(project, region, routerName).Do() + natIpsProp, err := expandComputeRouterNatNatIps(d.Get("nat_ips"), d, config) if err != nil { - if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { - return fmt.Errorf("router %s not found", routerName) - } - - return fmt.Errorf("Error reading parent router %s: %s", routerName, err) + return err + } else if v, ok := d.GetOkExists("nat_ips"); ok || !reflect.DeepEqual(v, natIpsProp) { + obj["natIps"] = natIpsProp } - - var nat *computeBeta.RouterNat - nats := router.Nats - for _, nat = range nats { - if nat.Name == natName { - break - } + sourceSubnetworkIpRangesToNatProp, err := expandComputeRouterNatSourceSubnetworkIpRangesToNat(d.Get("source_subnetwork_ip_ranges_to_nat"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("source_subnetwork_ip_ranges_to_nat"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, sourceSubnetworkIpRangesToNatProp)) { + obj["sourceSubnetworkIpRangesToNat"] = sourceSubnetworkIpRangesToNatProp } - if nat == nil || nat.Name != natName { - return fmt.Errorf("nat %s for router %s does not exist, cannot update", natName, routerName) + subnetworksProp, err := expandComputeRouterNatSubnetwork(d.Get("subnetwork"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("subnetwork"); ok || !reflect.DeepEqual(v, subnetworksProp) { + obj["subnetworks"] = subnetworksProp } - - nat.ForceSendFields = []string{ - "natIps", - "logConfig", - "subnetworks", - "minPortsPerVm", - "udpIdleTimeoutSec", - "icmpIdleTimeoutSec", - "tcpEstablishedIdleTimeoutSec", - "tcpTransitoryIdleTimeoutSec", + minPortsPerVmProp, err := expandComputeRouterNatMinPortsPerVm(d.Get("min_ports_per_vm"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("min_ports_per_vm"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, minPortsPerVmProp)) { + obj["minPortsPerVm"] = minPortsPerVmProp + } + udpIdleTimeoutSecProp, err := expandComputeRouterNatUdpIdleTimeoutSec(d.Get("udp_idle_timeout_sec"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("udp_idle_timeout_sec"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, udpIdleTimeoutSecProp)) { + obj["udpIdleTimeoutSec"] = udpIdleTimeoutSecProp + } + icmpIdleTimeoutSecProp, err := expandComputeRouterNatIcmpIdleTimeoutSec(d.Get("icmp_idle_timeout_sec"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("icmp_idle_timeout_sec"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, icmpIdleTimeoutSecProp)) { + obj["icmpIdleTimeoutSec"] = icmpIdleTimeoutSecProp + } + tcpEstablishedIdleTimeoutSecProp, err := expandComputeRouterNatTcpEstablishedIdleTimeoutSec(d.Get("tcp_established_idle_timeout_sec"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("tcp_established_idle_timeout_sec"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, tcpEstablishedIdleTimeoutSecProp)) { + obj["tcpEstablishedIdleTimeoutSec"] = tcpEstablishedIdleTimeoutSecProp + } + tcpTransitoryIdleTimeoutSecProp, err := expandComputeRouterNatTcpTransitoryIdleTimeoutSec(d.Get("tcp_transitory_idle_timeout_sec"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("tcp_transitory_idle_timeout_sec"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, tcpTransitoryIdleTimeoutSecProp)) { + obj["tcpTransitoryIdleTimeoutSec"] = tcpTransitoryIdleTimeoutSecProp + } + logConfigProp, err := expandComputeRouterNatLogConfig(d.Get("log_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("log_config"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, logConfigProp)) { + obj["logConfig"] = logConfigProp } - nat.MinPortsPerVm = int64(d.Get("min_ports_per_vm").(int)) - nat.UdpIdleTimeoutSec = int64(d.Get("udp_idle_timeout_sec").(int)) - nat.IcmpIdleTimeoutSec = int64(d.Get("icmp_idle_timeout_sec").(int)) - nat.TcpEstablishedIdleTimeoutSec = int64(d.Get("tcp_established_idle_timeout_sec").(int)) - nat.TcpTransitoryIdleTimeoutSec = int64(d.Get("tcp_transitory_idle_timeout_sec").(int)) - nat.NatIpAllocateOption = d.Get("nat_ip_allocate_option").(string) - nat.SourceSubnetworkIpRangesToNat = d.Get("source_subnetwork_ip_ranges_to_nat").(string) - - if v, ok := d.GetOk("nat_ips"); ok { - nat.NatIps = convertStringArr(v.(*schema.Set).List()) - } else { - nat.NatIps = []string{} + lockName, err := replaceVars(d, config, "router/{{region}}/{{router}}") + if err != nil { + return err } + mutexKV.Lock(lockName) + defer mutexKV.Unlock(lockName) - if v, ok := d.GetOk("subnetwork"); ok { - nat.Subnetworks = expandSubnetworks(v.(*schema.Set).List()) - } else { - nat.Subnetworks = []*computeBeta.RouterNatSubnetworkToNat{} + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/routers/{{router}}") + if err != nil { + return err } - if v, ok := d.GetOk("log_config"); ok { - nat.LogConfig = expandLogConfig(v) + log.Printf("[DEBUG] Updating RouterNat %q: %#v", d.Id(), obj) + + obj, err = resourceComputeRouterNatPatchUpdateEncoder(d, meta, obj) + if err != nil { + return err } + res, err := sendRequestWithTimeout(config, "PATCH", project, url, obj, d.Timeout(schema.TimeoutUpdate)) - log.Printf("[INFO] Updating nat %s: +%v", natName, nat) - obj := &computeBeta.Router{ - Nats: nats, + if err != nil { + return fmt.Errorf("Error updating RouterNat %q: %s", d.Id(), err) } - log.Printf("[DEBUG] Updating router %s/%s with nats: %+v", region, routerName, nats) - op, err := config.clientComputeBeta.Routers.Patch(project, region, router.Name, obj).Do() + op := &compute.Operation{} + err = Convert(res, op) if err != nil { - return fmt.Errorf("Error patching router %s/%s: %s", region, routerName, err) + return err } - d.SetId(fmt.Sprintf("%s/%s/%s/%s", project, region, routerName, natName)) - err = computeBetaOperationWaitTime(config.clientCompute, op, project, "Patching router", int(d.Timeout(schema.TimeoutCreate).Minutes())) + err = computeOperationWaitTime( + config.clientCompute, op, project, "Updating RouterNat", + int(d.Timeout(schema.TimeoutUpdate).Minutes())) + if err != nil { - d.SetId("") - return fmt.Errorf("Error while waiting to patch router %s/%s: %s", region, routerName, err) + return err } return resourceComputeRouterNatRead(d, meta) @@ -373,77 +480,62 @@ func resourceComputeRouterNatUpdate(d *schema.ResourceData, meta interface{}) er func resourceComputeRouterNatDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - region, err := getRegion(d, config) + project, err := getProject(d, config) if err != nil { return err } - project, err := getProject(d, config) + lockName, err := replaceVars(d, config, "router/{{region}}/{{router}}") if err != nil { return err } + mutexKV.Lock(lockName) + defer mutexKV.Unlock(lockName) - routerName := d.Get("router").(string) - natName := d.Get("name").(string) - - routerLock := getRouterLockName(region, routerName) - mutexKV.Lock(routerLock) - defer mutexKV.Unlock(routerLock) - - routersService := config.clientComputeBeta.Routers - router, err := routersService.Get(project, region, routerName).Do() + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/routers/{{router}}") if err != nil { - if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { - log.Printf("[WARN] Removing router nat %s because its router %s/%s is gone", natName, region, routerName) - - return nil - } - - return fmt.Errorf("Error Reading Router %s: %s", routerName, err) + return err } - var newNats []*computeBeta.RouterNat = make([]*computeBeta.RouterNat, 0, len(router.Nats)) - for _, nat := range router.Nats { - if nat.Name == natName { - continue - } else { - newNats = append(newNats, nat) - } - } + var obj map[string]interface{} - if len(newNats) == len(router.Nats) { - log.Printf("[DEBUG] Router %s/%s had no nat %s already", region, routerName, natName) - d.SetId("") - return nil - } - - log.Printf("[INFO] Removing nat %s from router %s/%s", natName, region, routerName) - patchRouter := &computeBeta.Router{ - Nats: newNats, + obj, err = resourceComputeRouterNatPatchDeleteEncoder(d, meta, obj) + if err != nil { + return handleNotFoundError(err, d, "RouterNat") } + log.Printf("[DEBUG] Deleting RouterNat %q", d.Id()) - if len(newNats) == 0 { - patchRouter.ForceSendFields = append(patchRouter.ForceSendFields, "Nats") + res, err := sendRequestWithTimeout(config, "PATCH", project, url, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "RouterNat") } - log.Printf("[DEBUG] Updating router %s/%s with nats: %+v", region, routerName, newNats) - op, err := routersService.Patch(project, region, router.Name, patchRouter).Do() + op := &compute.Operation{} + err = Convert(res, op) if err != nil { - return fmt.Errorf("Error patching router %s/%s: %s", region, routerName, err) + return err } - err = computeBetaOperationWaitTime(config.clientCompute, op, project, "Patching router", int(d.Timeout(schema.TimeoutDelete).Minutes())) + err = computeOperationWaitTime( + config.clientCompute, op, project, "Deleting RouterNat", + int(d.Timeout(schema.TimeoutDelete).Minutes())) + if err != nil { - return fmt.Errorf("Error waiting to patch router %s/%s: %s", region, routerName, err) + return err } - d.SetId("") + log.Printf("[DEBUG] Finished deleting RouterNat %q: %#v", d.Id(), res) return nil } func resourceComputeRouterNatImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { config := meta.(*Config) - if err := parseImportId([]string{"(?P[^/]+)/(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)"}, d, config); err != nil { + if err := parseImportId([]string{ + "projects/(?P[^/]+)/regions/(?P[^/]+)/routers/(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)", + }, d, config); err != nil { return nil, err } @@ -457,64 +549,428 @@ func resourceComputeRouterNatImport(d *schema.ResourceData, meta interface{}) ([ return []*schema.ResourceData{d}, nil } -func flattenRouterNatLogConfig(logConfig *computeBeta.RouterNatLogConfig) []map[string]interface{} { - result := make([]map[string]interface{}, 0, 1) - if logConfig != nil { - cfg := map[string]interface{}{} - cfg["filter"] = logConfig.Filter - cfg["enable"] = logConfig.Enable - result = append(result, cfg) +func flattenComputeRouterNatName(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenComputeRouterNatNatIpAllocateOption(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenComputeRouterNatNatIps(v interface{}, d *schema.ResourceData) interface{} { + if v == nil { + return v + } + return convertAndMapStringArr(v.([]interface{}), ConvertSelfLinkToV1) +} + +func flattenComputeRouterNatSourceSubnetworkIpRangesToNat(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenComputeRouterNatSubnetwork(v interface{}, d *schema.ResourceData) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := schema.NewSet(schema.HashResource(computeRouterNatSubnetworkSchema()), []interface{}{}) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed.Add(map[string]interface{}{ + "name": flattenComputeRouterNatSubnetworkName(original["name"], d), + "source_ip_ranges_to_nat": flattenComputeRouterNatSubnetworkSourceIpRangesToNat(original["sourceIpRangesToNat"], d), + "secondary_ip_range_names": flattenComputeRouterNatSubnetworkSecondaryIpRangeNames(original["secondaryIpRangeNames"], d), + }) + } + return transformed +} +func flattenComputeRouterNatSubnetworkName(v interface{}, d *schema.ResourceData) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + +func flattenComputeRouterNatSubnetworkSourceIpRangesToNat(v interface{}, d *schema.ResourceData) interface{} { + if v == nil { + return v + } + return schema.NewSet(schema.HashString, v.([]interface{})) +} + +func flattenComputeRouterNatSubnetworkSecondaryIpRangeNames(v interface{}, d *schema.ResourceData) interface{} { + if v == nil { + return v + } + return schema.NewSet(schema.HashString, v.([]interface{})) +} + +func flattenComputeRouterNatMinPortsPerVm(v interface{}, d *schema.ResourceData) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } // let terraform core handle it if we can't convert the string to an int. + } + return v +} + +func flattenComputeRouterNatUdpIdleTimeoutSec(v interface{}, d *schema.ResourceData) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } // let terraform core handle it if we can't convert the string to an int. + } + return v +} + +func flattenComputeRouterNatIcmpIdleTimeoutSec(v interface{}, d *schema.ResourceData) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } // let terraform core handle it if we can't convert the string to an int. + } + return v +} + +func flattenComputeRouterNatTcpEstablishedIdleTimeoutSec(v interface{}, d *schema.ResourceData) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } // let terraform core handle it if we can't convert the string to an int. + } + return v +} + +func flattenComputeRouterNatTcpTransitoryIdleTimeoutSec(v interface{}, d *schema.ResourceData) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } // let terraform core handle it if we can't convert the string to an int. } - return result + return v } -func expandLogConfig(logConfigs interface{}) *computeBeta.RouterNatLogConfig { - configs := logConfigs.([]interface{}) - if len(configs) == 0 || configs[0] == nil { +func flattenComputeRouterNatLogConfig(v interface{}, d *schema.ResourceData) interface{} { + if v == nil { return nil } - cfg := configs[0].(map[string]interface{}) - result := computeBeta.RouterNatLogConfig{ - Filter: cfg["filter"].(string), - Enable: cfg["enable"].(bool), + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil } - return &result + transformed := make(map[string]interface{}) + transformed["enable"] = + flattenComputeRouterNatLogConfigEnable(original["enable"], d) + transformed["filter"] = + flattenComputeRouterNatLogConfigFilter(original["filter"], d) + return []interface{}{transformed} +} +func flattenComputeRouterNatLogConfigEnable(v interface{}, d *schema.ResourceData) interface{} { + return v } -func expandSubnetworks(subnetworks []interface{}) []*computeBeta.RouterNatSubnetworkToNat { - result := make([]*computeBeta.RouterNatSubnetworkToNat, 0, len(subnetworks)) +func flattenComputeRouterNatLogConfigFilter(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func expandComputeRouterNatName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} - for _, subnetwork := range subnetworks { - snm := subnetwork.(map[string]interface{}) - subnetworkToNat := computeBeta.RouterNatSubnetworkToNat{ - Name: snm["name"].(string), - SourceIpRangesToNat: convertStringSet(snm["source_ip_ranges_to_nat"].(*schema.Set)), +func expandComputeRouterNatNatIpAllocateOption(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRouterNatNatIps(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + v = v.(*schema.Set).List() + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + f, err := parseRegionalFieldValue("addresses", raw.(string), "project", "region", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for nat_ips: %s", err) + } + req = append(req, f.RelativeLink()) + } + return req, nil +} + +func expandComputeRouterNatSourceSubnetworkIpRangesToNat(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRouterNatSubnetwork(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + v = v.(*schema.Set).List() + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue } - if v, ok := snm["secondary_ip_range_names"]; ok { - subnetworkToNat.SecondaryIpRangeNames = convertStringSet(v.(*schema.Set)) + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedName, err := expandComputeRouterNatSubnetworkName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName } - result = append(result, &subnetworkToNat) + + transformedSourceIpRangesToNat, err := expandComputeRouterNatSubnetworkSourceIpRangesToNat(original["source_ip_ranges_to_nat"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSourceIpRangesToNat); val.IsValid() && !isEmptyValue(val) { + transformed["sourceIpRangesToNat"] = transformedSourceIpRangesToNat + } + + transformedSecondaryIpRangeNames, err := expandComputeRouterNatSubnetworkSecondaryIpRangeNames(original["secondary_ip_range_names"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSecondaryIpRangeNames); val.IsValid() && !isEmptyValue(val) { + transformed["secondaryIpRangeNames"] = transformedSecondaryIpRangeNames + } + + req = append(req, transformed) + } + return req, nil +} + +func expandComputeRouterNatSubnetworkName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + f, err := parseRegionalFieldValue("subnetworks", v.(string), "project", "region", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for name: %s", err) } + return f.RelativeLink(), nil +} + +func expandComputeRouterNatSubnetworkSourceIpRangesToNat(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + v = v.(*schema.Set).List() + return v, nil +} + +func expandComputeRouterNatSubnetworkSecondaryIpRangeNames(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + v = v.(*schema.Set).List() + return v, nil +} - return result +func expandComputeRouterNatMinPortsPerVm(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil } -func flattenRouterNatSubnetworkToNatBeta(subnetworksToNat []*computeBeta.RouterNatSubnetworkToNat) []map[string]interface{} { - result := make([]map[string]interface{}, 0, len(subnetworksToNat)) - for _, subnetworkToNat := range subnetworksToNat { - stnMap := make(map[string]interface{}) - stnMap["name"] = ConvertSelfLinkToV1(subnetworkToNat.Name) - stnMap["source_ip_ranges_to_nat"] = schema.NewSet(schema.HashString, convertStringArrToInterface(subnetworkToNat.SourceIpRangesToNat)) - stnMap["secondary_ip_range_names"] = schema.NewSet(schema.HashString, convertStringArrToInterface(subnetworkToNat.SecondaryIpRangeNames)) - result = append(result, stnMap) +func expandComputeRouterNatUdpIdleTimeoutSec(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRouterNatIcmpIdleTimeoutSec(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRouterNatTcpEstablishedIdleTimeoutSec(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRouterNatTcpTransitoryIdleTimeoutSec(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRouterNatLogConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedEnable, err := expandComputeRouterNatLogConfigEnable(original["enable"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnable); val.IsValid() && !isEmptyValue(val) { + transformed["enable"] = transformedEnable } - return result + + transformedFilter, err := expandComputeRouterNatLogConfigFilter(original["filter"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFilter); val.IsValid() && !isEmptyValue(val) { + transformed["filter"] = transformedFilter + } + + return transformed, nil +} + +func expandComputeRouterNatLogConfigEnable(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRouterNatLogConfigFilter(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil } -func convertSelfLinksToV1(selfLinks []string) []string { - result := make([]string, 0, len(selfLinks)) - for _, selfLink := range selfLinks { - result = append(result, ConvertSelfLinkToV1(selfLink)) +func flattenNestedComputeRouterNat(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) { + var v interface{} + var ok bool + + v, ok = res["nats"] + if !ok || v == nil { + return nil, nil + } + + switch v.(type) { + case []interface{}: + break + case map[string]interface{}: + // Construct list out of single nested resource + v = []interface{}{v} + default: + return nil, fmt.Errorf("expected list or map for value nats. Actual value: %v", v) + } + + _, item, err := resourceComputeRouterNatFindNestedObjectInList(d, meta, v.([]interface{})) + if err != nil { + return nil, err + } + return item, nil +} + +func resourceComputeRouterNatFindNestedObjectInList(d *schema.ResourceData, meta interface{}, items []interface{}) (index int, item map[string]interface{}, err error) { + expectedName, err := expandComputeRouterNatName(d.Get("name"), d, meta.(*Config)) + if err != nil { + return -1, nil, err + } + + // Search list for this resource. + for idx, itemRaw := range items { + if itemRaw == nil { + continue + } + item := itemRaw.(map[string]interface{}) + + itemName := flattenComputeRouterNatName(item["name"], d) + if !reflect.DeepEqual(itemName, expectedName) { + log.Printf("[DEBUG] Skipping item with name= %#v, looking for %#v)", itemName, expectedName) + continue + } + log.Printf("[DEBUG] Found item for resource %q: %#v)", d.Id(), item) + return idx, item, nil + } + return -1, nil, nil +} + +// PatchCreateEncoder handles creating request data to PATCH parent resource +// with list including new object. +func resourceComputeRouterNatPatchCreateEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { + currItems, err := resourceComputeRouterNatListForPatch(d, meta) + if err != nil { + return nil, err + } + + _, found, err := resourceComputeRouterNatFindNestedObjectInList(d, meta, currItems) + if err != nil { + return nil, err + } + + // Return error if item already created. + if found != nil { + return nil, fmt.Errorf("Unable to create RouterNat, existing object already found: %+v", found) + } + + // Return list with the resource to create appended + return map[string]interface{}{ + "nats": append(currItems, obj), + }, nil +} + +// PatchUpdateEncoder handles creating request data to PATCH parent resource +// with list including updated object. +func resourceComputeRouterNatPatchUpdateEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { + items, err := resourceComputeRouterNatListForPatch(d, meta) + if err != nil { + return nil, err + } + + idx, item, err := resourceComputeRouterNatFindNestedObjectInList(d, meta, items) + if err != nil { + return nil, err + } + + // Return error if item to update does not exist. + if item == nil { + return nil, fmt.Errorf("Unable to update RouterNat %q - not found in list", d.Id()) + } + + // Merge new object into old. + for k, v := range obj { + item[k] = v + } + items[idx] = item + + // Return list with new item added + return map[string]interface{}{ + "nats": items, + }, nil +} + +// PatchDeleteEncoder handles creating request data to PATCH parent resource +// with list excluding object to delete. +func resourceComputeRouterNatPatchDeleteEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { + currItems, err := resourceComputeRouterNatListForPatch(d, meta) + if err != nil { + return nil, err + } + + idx, item, err := resourceComputeRouterNatFindNestedObjectInList(d, meta, currItems) + if err != nil { + return nil, err + } + if item == nil { + // Spoof 404 error for proper handling by Delete (i.e. no-op) + return nil, &googleapi.Error{ + Code: 404, + Message: "RouterNat not found in list", + } + } + + updatedItems := append(currItems[:idx], currItems[idx+1:]...) + return map[string]interface{}{ + "nats": updatedItems, + }, nil +} + +// ListForPatch handles making API request to get parent resource and +// extracting list of objects. +func resourceComputeRouterNatListForPatch(d *schema.ResourceData, meta interface{}) ([]interface{}, error) { + config := meta.(*Config) + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/routers/{{router}}") + if err != nil { + return nil, err + } + project, err := getProject(d, config) + if err != nil { + return nil, err + } + res, err := sendRequest(config, "GET", project, url, nil) + if err != nil { + return nil, err + } + + v, ok := res["nats"] + if ok && v != nil { + ls, lsOk := v.([]interface{}) + if !lsOk { + return nil, fmt.Errorf(`expected list for nested field "nats"`) + } + return ls, nil } - return result + return nil, nil } diff --git a/google/resource_resource_manager_lien.go b/google/resource_resource_manager_lien.go index c727c61d2e8..7cd737c004a 100644 --- a/google/resource_resource_manager_lien.go +++ b/google/resource_resource_manager_lien.go @@ -311,11 +311,18 @@ func flattenNestedResourceManagerLien(d *schema.ResourceData, meta interface{}, return nil, fmt.Errorf("expected list or map for value liens. Actual value: %v", v) } + _, item, err := resourceResourceManagerLienFindNestedObjectInList(d, meta, v.([]interface{})) + if err != nil { + return nil, err + } + return item, nil +} + +func resourceResourceManagerLienFindNestedObjectInList(d *schema.ResourceData, meta interface{}, items []interface{}) (index int, item map[string]interface{}, err error) { expectedName := d.Get("name") // Search list for this resource. - items := v.([]interface{}) - for _, itemRaw := range items { + for idx, itemRaw := range items { if itemRaw == nil { continue } @@ -324,7 +331,7 @@ func flattenNestedResourceManagerLien(d *schema.ResourceData, meta interface{}, // Decode list item before comparing. item, err := resourceResourceManagerLienDecoder(d, meta, item) if err != nil { - return nil, err + return -1, nil, err } itemName := flattenResourceManagerLienName(item["name"], d) @@ -333,12 +340,10 @@ func flattenNestedResourceManagerLien(d *schema.ResourceData, meta interface{}, continue } log.Printf("[DEBUG] Found item for resource %q: %#v)", d.Id(), item) - return item, nil + return idx, item, nil } - - return nil, nil + return -1, nil, nil } - func resourceResourceManagerLienDecoder(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) { // The problem we're trying to solve here is that this property is a Project, // and there are a lot of ways to specify a Project, including the ID vs diff --git a/website/docs/r/compute_router_nat.html.markdown b/website/docs/r/compute_router_nat.html.markdown index e9c16cb1303..a4bc1420833 100644 --- a/website/docs/r/compute_router_nat.html.markdown +++ b/website/docs/r/compute_router_nat.html.markdown @@ -1,92 +1,113 @@ --- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- layout: "google" page_title: "Google: google_compute_router_nat" sidebar_current: "docs-google-compute-router-nat" description: |- - Manages a Cloud NAT. + A NAT service created in a router. --- # google\_compute\_router\_nat -Manages a Cloud NAT. For more information see -[the official documentation](https://cloud.google.com/nat/docs/overview) -and -[API](https://cloud.google.com/compute/docs/reference/rest/beta/routers). +A NAT service created in a router. -## Example Usage -A simple NAT configuration: enable NAT for all Subnetworks associated with -the Network associated with the given Router. +To get more information about RouterNat, see: + +* [API documentation](https://cloud.google.com/compute/docs/reference/rest/v1/routers) +* How-to Guides + * [Google Cloud Router](https://cloud.google.com/router/docs/) + +## Example Usage - Router Nat Basic + ```hcl -resource "google_compute_network" "default" { - name = "my-network" +resource "google_compute_network" "net" { + name = "my-network" } -resource "google_compute_subnetwork" "default" { - name = "my-subnet" - network = google_compute_network.default.self_link - ip_cidr_range = "10.0.0.0/16" - region = "us-central1" +resource "google_compute_subnetwork" "subnet" { + name = "my-subnetwork" + network = google_compute_network.net.self_link + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" } -resource "google_compute_router" "router" { - name = "router" - region = google_compute_subnetwork.default.region - network = google_compute_network.default.self_link +resource "google_compute_router" "router"{ + name = "my-router" + region = google_compute_subnetwork.subnet.region + network = google_compute_network.net.self_link + + bgp { + asn = 64514 + } } -resource "google_compute_router_nat" "simple-nat" { - name = "nat-1" - router = google_compute_router.router.name - region = "us-central1" - nat_ip_allocate_option = "AUTO_ONLY" - source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES" +resource "google_compute_router_nat" "nat" { + name = "my-router-nat" + router = google_compute_router.router.name + region = google_compute_router.router.region + nat_ip_allocate_option = "AUTO_ONLY" + source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES" + + log_config { + enable = true + filter = "ERRORS_ONLY" + } } ``` +## Example Usage - Router Nat Manual Ips -A production-like configuration: enable NAT for one Subnetwork and use a list of -static external IP addresses. ```hcl -resource "google_compute_network" "default" { - name = "my-network" +resource "google_compute_network" "net" { + name = "my-network" } -resource "google_compute_subnetwork" "default" { - name = "my-subnet" - network = google_compute_network.default.self_link - ip_cidr_range = "10.0.0.0/16" - region = "us-central1" +resource "google_compute_subnetwork" "subnet" { + name = "my-subnetwork" + network = google_compute_network.net.self_link + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" } -resource "google_compute_router" "router" { - name = "router" - region = google_compute_subnetwork.default.region - network = google_compute_network.default.self_link +resource "google_compute_router" "router"{ + name = "my-router" + region = google_compute_subnetwork.subnet.region + network = google_compute_network.net.self_link } resource "google_compute_address" "address" { - count = 2 - name = "nat-external-address-${count.index}" - region = "us-central1" + count = 2 + name = "nat-manual-ip-${count.index}" + region = google_compute_subnetwork.subnet.region } -resource "google_compute_router_nat" "advanced-nat" { - name = "nat-1" - router = google_compute_router.router.name - region = "us-central1" - nat_ip_allocate_option = "MANUAL_ONLY" - nat_ips = google_compute_address.address[*].self_link - source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS" - subnetwork { - name = google_compute_subnetwork.default.self_link - source_ip_ranges_to_nat = ["ALL_IP_RANGES"] - } - log_config { - filter = "TRANSLATIONS_ONLY" - enable = true - } +resource "google_compute_router_nat" "nat_manual" { + name = "my-router-nat" + router = google_compute_router.router.name + region = google_compute_router.router.region + + nat_ip_allocate_option = "MANUAL_ONLY" + nat_ips = google_compute_address.address[*].self_link + + source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS" + subnetwork { + name = google_compute_subnetwork.default.self_link + source_ip_ranges_to_nat = ["ALL_IP_RANGES"] + } } ``` @@ -94,75 +115,130 @@ resource "google_compute_router_nat" "advanced-nat" { The following arguments are supported: -* `name` - (Required) A unique name for Cloud NAT, required by GCE. Changing - this forces a new NAT to be created. -* `router` - (Required) The name of the router in which this NAT will be configured. - Changing this forces a new NAT to be created. +* `name` - + (Required) + Name of the NAT service. The name must be 1-63 characters long and + comply with RFC1035. -* `nat_ip_allocate_option` - (Required) How external IPs should be allocated for - this NAT. Valid values are `AUTO_ONLY` or `MANUAL_ONLY`. Changing this forces - a new NAT to be created. +* `nat_ip_allocate_option` - + (Required) + How external IPs should be allocated for this NAT. Valid values are + `AUTO_ONLY` for only allowing NAT IPs allocated by Google Cloud + Platform, or `MANUAL_ONLY` for only user-allocated NAT IP addresses. + +* `source_subnetwork_ip_ranges_to_nat` - + (Required) + How NAT should be configured per Subnetwork. + If `ALL_SUBNETWORKS_ALL_IP_RANGES`, all of the + IP ranges in every Subnetwork are allowed to Nat. + If `ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES`, all of the primary IP + ranges in every Subnetwork are allowed to Nat. + `LIST_OF_SUBNETWORKS`: A list of Subnetworks are allowed to Nat + (specified in the field subnetwork below). Note that if this field + contains ALL_SUBNETWORKS_ALL_IP_RANGES or + ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES, then there should not be any + other RouterNat section in any Router for this network in this region. + +* `router` - + (Required) + The name of the Cloud Router in which this NAT will be configured. -* `source_subnetwork_ip_ranges_to_nat` - (Required) How NAT should be configured - per Subnetwork. Valid values include: `ALL_SUBNETWORKS_ALL_IP_RANGES`, - `ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES`, `LIST_OF_SUBNETWORKS`. - - - -* `project` - (Optional) The ID of the project in which this NAT's router belongs. If it - is not provided, the provider project is used. Changing this forces a new NAT to be created. -* `region` - (Optional) The region this NAT's router sits in. If not specified, - the project region will be used. Changing this forces a new NAT to be - created. +* `nat_ips` - + (Optional) + Self-links of NAT IPs. Only valid if natIpAllocateOption + is set to MANUAL_ONLY. + +* `subnetwork` - + (Optional) + One or more subnetwork NAT configurations. Only used if + `source_subnetwork_ip_ranges_to_nat` is set to `LIST_OF_SUBNETWORKS` Structure is documented below. + +* `min_ports_per_vm` - + (Optional) + Minimum number of ports allocated to a VM from this NAT. -* `nat_ips` - (Optional) List of `self_link`s of external IPs. Only valid if - `nat_ip_allocate_option` is set to `MANUAL_ONLY`. +* `udp_idle_timeout_sec` - + (Optional) + Timeout (in seconds) for UDP connections. Defaults to 30s if not set. -* `subnetwork` - (Optional) One or more subnetwork NAT configurations. Only used - if `source_subnetwork_ip_ranges_to_nat` is set to `LIST_OF_SUBNETWORKS`. See - the section below for details on configuration. +* `icmp_idle_timeout_sec` - + (Optional) + Timeout (in seconds) for ICMP connections. Defaults to 30s if not set. -* `min_ports_per_vm` - (Optional) Minimum number of ports allocated to a VM - from this NAT config. If not set, a default number of ports is allocated to a VM. +* `tcp_established_idle_timeout_sec` - + (Optional) + Timeout (in seconds) for TCP established connections. + Defaults to 1200s if not set. -* `udp_idle_timeout_sec` - (Optional) Timeout (in seconds) for UDP connections. - Defaults to 30s if not set. +* `tcp_transitory_idle_timeout_sec` - + (Optional) + Timeout (in seconds) for TCP transitory connections. + Defaults to 30s if not set. -* `icmp_idle_timeout_sec` - (Optional) Timeout (in seconds) for ICMP connections. - Defaults to 30s if not set. +* `log_config` - + (Optional) + Configuration for logging on NAT Structure is documented below. -* `tcp_established_idle_timeout_sec` - (Optional) Timeout (in seconds) for TCP - established connections. Defaults to 1200s if not set. +* `region` - + (Optional) + Region where the router and NAT reside. + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. -* `tcp_transitory_idle_timeout_sec` - (Optional) Timeout (in seconds) for TCP - transitory connections. Defaults to 30s if not set. The `subnetwork` block supports: -* `name` - (Required) The `self_link` of the subnetwork to NAT. +* `name` - + (Required) + Self-link of subnetwork to NAT -* `source_ip_ranges_to_nat` - (Required) List of options for which source IPs in the subnetwork - should have NAT enabled. Supported values include: `ALL_IP_RANGES`, - `LIST_OF_SECONDARY_IP_RANGES`, `PRIMARY_IP_RANGE` +* `source_ip_ranges_to_nat` - + (Required) + List of options for which source IPs in the subnetwork + should have NAT enabled. Supported values include: + `ALL_IP_RANGES`, `LIST_OF_SECONDARY_IP_RANGES`, + `PRIMARY_IP_RANGE`. -* `secondary_ip_range_names` - (Optional) List of the secondary ranges of the subnetwork - that are allowed to use NAT. This can be populated only if - `LIST_OF_SECONDARY_IP_RANGES` is one of the values in `source_ip_ranges_to_nat`. +* `secondary_ip_range_names` - + (Optional) + List of the secondary ranges of the subnetwork that are allowed + to use NAT. This can be populated only if + `LIST_OF_SECONDARY_IP_RANGES` is one of the values in + sourceIpRangesToNat The `log_config` block supports: -* `filter` - (Required) Specifies the desired filtering of logs on this NAT. - Valid values include: `ALL`, `ERRORS_ONLY`, `TRANSLATIONS_ONLY` +* `enable` - + (Required) + Indicates whether or not to export logs. + +* `filter` - + (Required) + Specifies the desired filtering of logs on this NAT. Valid + values are: `"ERRORS_ONLY"`, `"TRANSLATIONS_ONLY"`, `"ALL"` + + +## Timeouts -* `enable` - (Required) Whether to export logs. +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 10 minutes. +- `update` - Default is 10 minutes. +- `delete` - Default is 10 minutes. ## Import -Router NATs can be imported using any of these accepted formats: +RouterNat can be imported using any of these accepted formats: ``` +$ terraform import google_compute_router_nat.default projects/{{project}}/regions/{{region}}/routers/{{router}}/{{name}} $ terraform import google_compute_router_nat.default {{project}}/{{region}}/{{router}}/{{name}} $ terraform import google_compute_router_nat.default {{region}}/{{router}}/{{name}} $ terraform import google_compute_router_nat.default {{router}}/{{name}} @@ -170,3 +246,7 @@ $ terraform import google_compute_router_nat.default {{router}}/{{name}} -> If you're importing a resource with beta features, make sure to include `-provider=google-beta` as an argument so that Terraform uses the correct provider to import your resource. + +## User Project Overrides + +This resource supports [User Project Overrides](https://www.terraform.io/docs/providers/google/provider_reference.html#user_project_override).