terraform-provider-google/google/resource_compute_router_nat.go
The Magician a3caf90b4f Make google_compute_router_nat's source_ip_ranges_to_nat Required (#2749)
<!-- This change is generated by MagicModules. -->
/cc @rileykarson
2018-12-26 18:00:46 -08:00

393 lines
11 KiB
Go

package google
import (
"fmt"
"log"
"time"
"strings"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/googleapi"
)
var (
routerNatSubnetworkConfig = &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: 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,
Required: true,
MinItems: 1,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"secondary_ip_range_names": {
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
}
)
func resourceComputeRouterNat() *schema.Resource {
return &schema.Resource{
// TODO(https://github.com/GoogleCloudPlatform/magic-modules/issues/963): Implement Update
Create: resourceComputeRouterNatCreate,
Read: resourceComputeRouterNatRead,
Delete: resourceComputeRouterNatDelete,
Importer: &schema.ResourceImporter{
State: resourceComputeRouterNatImportState,
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(10 * time.Minute),
Delete: schema.DefaultTimeout(10 * time.Minute),
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateRFC1035Name(2, 63),
},
"router": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"nat_ip_allocate_option": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{"MANUAL_ONLY", "AUTO_ONLY"}, false),
},
"nat_ips": {
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"source_subnetwork_ip_ranges_to_nat": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{"ALL_SUBNETWORKS_ALL_IP_RANGES", "ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES", "LIST_OF_SUBNETWORKS"}, false),
},
"subnetwork": {
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
Elem: routerNatSubnetworkConfig,
},
"min_ports_per_vm": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"udp_idle_timeout_sec": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"icmp_idle_timeout_sec": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"tcp_established_idle_timeout_sec": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"tcp_transitory_idle_timeout_sec": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"project": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"region": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
},
}
}
func resourceComputeRouterNatCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region, err := getRegion(d, config)
if err != nil {
return err
}
project, err := getProject(d, config)
if err != nil {
return err
}
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()
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)
}
nats := router.Nats
for _, nat := range nats {
if nat.Name == natName {
return fmt.Errorf("Router %s has nat %s already", routerName, natName)
}
}
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)),
}
if v, ok := d.GetOk("subnetwork"); ok {
nat.Subnetworks = expandSubnetworks(v.(*schema.Set).List())
}
log.Printf("[INFO] Adding nat %s", natName)
nats = append(nats, nat)
patchRouter := &computeBeta.Router{
Nats: nats,
}
log.Printf("[DEBUG] Updating router %s/%s with nats: %+v", region, routerName, nats)
op, err := routersService.Patch(project, region, router.Name, patchRouter).Do()
if err != nil {
return fmt.Errorf("Error patching router %s/%s: %s", region, routerName, err)
}
d.SetId(fmt.Sprintf("%s/%s/%s", region, routerName, natName))
err = computeBetaOperationWaitTime(config.clientCompute, op, project, "Patching router", int(d.Timeout(schema.TimeoutCreate).Minutes()))
if err != nil {
d.SetId("")
return fmt.Errorf("Error waiting to patch router %s/%s: %s", region, routerName, err)
}
return resourceComputeRouterNatRead(d, meta)
}
func resourceComputeRouterNatRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region, err := getRegion(d, config)
if err != nil {
return err
}
project, err := getProject(d, config)
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()
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)
}
for _, nat := range router.Nats {
if nat.Name == natName {
d.SetId(fmt.Sprintf("%s/%s/%s", 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)
if err := d.Set("subnetwork", flattenRouterNatSubnetworkToNatBeta(nat.Subnetworks)); err != nil {
return fmt.Errorf("Error reading router nat: %s", err)
}
return nil
}
}
log.Printf("[WARN] Removing router nat %s/%s/%s because it is gone", region, routerName, natName)
d.SetId("")
return nil
}
func resourceComputeRouterNatDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region, err := getRegion(d, config)
if err != nil {
return err
}
project, err := getProject(d, config)
if err != nil {
return err
}
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()
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)
}
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)
}
}
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,
}
if len(newNats) == 0 {
patchRouter.ForceSendFields = append(patchRouter.ForceSendFields, "Nats")
}
log.Printf("[DEBUG] Updating router %s/%s with nats: %+v", region, routerName, newNats)
op, err := routersService.Patch(project, region, router.Name, patchRouter).Do()
if err != nil {
return fmt.Errorf("Error patching router %s/%s: %s", region, routerName, err)
}
err = computeBetaOperationWaitTime(config.clientCompute, op, project, "Patching router", int(d.Timeout(schema.TimeoutDelete).Minutes()))
if err != nil {
return fmt.Errorf("Error waiting to patch router %s/%s: %s", region, routerName, err)
}
d.SetId("")
return nil
}
func resourceComputeRouterNatImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
parts := strings.Split(d.Id(), "/")
if len(parts) != 3 {
return nil, fmt.Errorf("Invalid router nat specifier. Expecting {region}/{router}/{nat}")
}
d.Set("region", parts[0])
d.Set("router", parts[1])
d.Set("name", parts[2])
return []*schema.ResourceData{d}, nil
}
func expandSubnetworks(subnetworks []interface{}) []*computeBeta.RouterNatSubnetworkToNat {
result := make([]*computeBeta.RouterNatSubnetworkToNat, 0, len(subnetworks))
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)),
}
if v, ok := snm["secondary_ip_range_names"]; ok {
subnetworkToNat.SecondaryIpRangeNames = convertStringSet(v.(*schema.Set))
}
result = append(result, &subnetworkToNat)
}
return result
}
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)
}
return result
}
func convertSelfLinksToV1(selfLinks []string) []string {
result := make([]string, 0, len(selfLinks))
for _, selfLink := range selfLinks {
result = append(result, ConvertSelfLinkToV1(selfLink))
}
return result
}