Skip to content

Commit b341899

Browse files
modular-magicianpaddycarver
authored andcommitted
Add router nat resource. For TPG #2249. (#2576)
1 parent b27e426 commit b341899

File tree

3 files changed

+608
-2
lines changed

3 files changed

+608
-2
lines changed

google/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ func Provider() terraform.ResourceProvider {
146146
"google_compute_route": resourceComputeRoute(),
147147
"google_compute_router": resourceComputeRouter(),
148148
"google_compute_router_interface": resourceComputeRouterInterface(),
149+
"google_compute_router_nat": resourceComputeRouterNat(),
149150
"google_compute_router_peer": resourceComputeRouterPeer(),
150151
"google_compute_security_policy": resourceComputeSecurityPolicy(),
151152
"google_compute_shared_vpc_host_project": resourceComputeSharedVpcHostProject(),

google/resource_compute_router_nat.go

+385-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,387 @@
11
package google
22

3-
// Magic Modules doesn't let us remove files - blank out beta-only common-compile files for now.
3+
import (
4+
"fmt"
5+
"log"
6+
"time"
7+
8+
"strings"
9+
10+
"github.com/hashicorp/terraform/helper/schema"
11+
"github.com/hashicorp/terraform/helper/validation"
12+
computeBeta "google.golang.org/api/compute/v0.beta"
13+
14+
"google.golang.org/api/googleapi"
15+
)
16+
17+
var (
18+
routerNatSubnetworkConfig = &schema.Resource{
19+
Schema: map[string]*schema.Schema{
20+
"name": &schema.Schema{
21+
Type: schema.TypeString,
22+
Required: true,
23+
ForceNew: true,
24+
},
25+
"source_ip_ranges_to_nat": &schema.Schema{
26+
Type: schema.TypeSet,
27+
Optional: true,
28+
ForceNew: true,
29+
Elem: &schema.Schema{Type: schema.TypeString},
30+
},
31+
"secondary_ip_range_names": &schema.Schema{
32+
Type: schema.TypeSet,
33+
Optional: true,
34+
ForceNew: true,
35+
Elem: &schema.Schema{Type: schema.TypeString},
36+
},
37+
},
38+
}
39+
)
40+
41+
func resourceComputeRouterNat() *schema.Resource {
42+
return &schema.Resource{
43+
// TODO(https://github.com/GoogleCloudPlatform/magic-modules/issues/963): Implement Update
44+
Create: resourceComputeRouterNatCreate,
45+
Read: resourceComputeRouterNatRead,
46+
Delete: resourceComputeRouterNatDelete,
47+
Importer: &schema.ResourceImporter{
48+
State: resourceComputeRouterNatImportState,
49+
},
50+
51+
Timeouts: &schema.ResourceTimeout{
52+
Create: schema.DefaultTimeout(10 * time.Minute),
53+
Delete: schema.DefaultTimeout(10 * time.Minute),
54+
},
55+
56+
Schema: map[string]*schema.Schema{
57+
"name": &schema.Schema{
58+
Type: schema.TypeString,
59+
Required: true,
60+
ForceNew: true,
61+
ValidateFunc: validateRFC1035Name(2, 63),
62+
},
63+
"router": &schema.Schema{
64+
Type: schema.TypeString,
65+
Required: true,
66+
ForceNew: true,
67+
},
68+
"nat_ip_allocate_option": &schema.Schema{
69+
Type: schema.TypeString,
70+
Required: true,
71+
ForceNew: true,
72+
ValidateFunc: validation.StringInSlice([]string{"MANUAL_ONLY", "AUTO_ONLY"}, false),
73+
},
74+
"nat_ips": &schema.Schema{
75+
Type: schema.TypeSet,
76+
Optional: true,
77+
ForceNew: true,
78+
Elem: &schema.Schema{Type: schema.TypeString},
79+
},
80+
"source_subnetwork_ip_ranges_to_nat": &schema.Schema{
81+
Type: schema.TypeString,
82+
Optional: true,
83+
ForceNew: true,
84+
ValidateFunc: validation.StringInSlice([]string{"ALL_SUBNETWORKS_ALL_IP_RANGES", "ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES", "LIST_OF_SUBNETWORKS"}, false),
85+
},
86+
"subnetwork": &schema.Schema{
87+
Type: schema.TypeSet,
88+
Optional: true,
89+
ForceNew: true,
90+
Elem: routerNatSubnetworkConfig,
91+
},
92+
"min_ports_per_vm": &schema.Schema{
93+
Type: schema.TypeInt,
94+
Optional: true,
95+
ForceNew: true,
96+
},
97+
"udp_idle_timeout_sec": &schema.Schema{
98+
Type: schema.TypeInt,
99+
Optional: true,
100+
ForceNew: true,
101+
},
102+
"icmp_idle_timeout_sec": &schema.Schema{
103+
Type: schema.TypeInt,
104+
Optional: true,
105+
ForceNew: true,
106+
},
107+
"tcp_established_idle_timeout_sec": &schema.Schema{
108+
Type: schema.TypeInt,
109+
Optional: true,
110+
ForceNew: true,
111+
},
112+
"tcp_transitory_idle_timeout_sec": &schema.Schema{
113+
Type: schema.TypeInt,
114+
Optional: true,
115+
ForceNew: true,
116+
},
117+
"project": &schema.Schema{
118+
Type: schema.TypeString,
119+
Optional: true,
120+
Computed: true,
121+
ForceNew: true,
122+
},
123+
"region": &schema.Schema{
124+
Type: schema.TypeString,
125+
Optional: true,
126+
Computed: true,
127+
ForceNew: true,
128+
},
129+
},
130+
}
131+
}
132+
133+
func resourceComputeRouterNatCreate(d *schema.ResourceData, meta interface{}) error {
134+
135+
config := meta.(*Config)
136+
137+
region, err := getRegion(d, config)
138+
if err != nil {
139+
return err
140+
}
141+
142+
project, err := getProject(d, config)
143+
if err != nil {
144+
return err
145+
}
146+
147+
routerName := d.Get("router").(string)
148+
natName := d.Get("name").(string)
149+
150+
routerLock := getRouterLockName(region, routerName)
151+
mutexKV.Lock(routerLock)
152+
defer mutexKV.Unlock(routerLock)
153+
154+
routersService := config.clientComputeBeta.Routers
155+
router, err := routersService.Get(project, region, routerName).Do()
156+
if err != nil {
157+
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
158+
return fmt.Errorf("Router %s/%s not found", region, routerName)
159+
}
160+
161+
return fmt.Errorf("Error Reading router %s/%s: %s", region, routerName, err)
162+
}
163+
164+
nats := router.Nats
165+
for _, nat := range nats {
166+
if nat.Name == natName {
167+
return fmt.Errorf("Router %s has nat %s already", routerName, natName)
168+
}
169+
}
170+
171+
nat := &computeBeta.RouterNat{
172+
Name: natName,
173+
NatIpAllocateOption: d.Get("nat_ip_allocate_option").(string),
174+
NatIps: convertStringArr(d.Get("nat_ips").(*schema.Set).List()),
175+
SourceSubnetworkIpRangesToNat: d.Get("source_subnetwork_ip_ranges_to_nat").(string),
176+
MinPortsPerVm: int64(d.Get("min_ports_per_vm").(int)),
177+
UdpIdleTimeoutSec: int64(d.Get("udp_idle_timeout_sec").(int)),
178+
IcmpIdleTimeoutSec: int64(d.Get("icmp_idle_timeout_sec").(int)),
179+
TcpEstablishedIdleTimeoutSec: int64(d.Get("tcp_established_idle_timeout_sec").(int)),
180+
TcpTransitoryIdleTimeoutSec: int64(d.Get("tcp_transitory_idle_timeout_sec").(int)),
181+
}
182+
183+
if v, ok := d.GetOk("subnetwork"); ok {
184+
nat.Subnetworks = expandSubnetworks(v.(*schema.Set).List())
185+
}
186+
187+
log.Printf("[INFO] Adding nat %s", natName)
188+
nats = append(nats, nat)
189+
patchRouter := &computeBeta.Router{
190+
Nats: nats,
191+
}
192+
193+
log.Printf("[DEBUG] Updating router %s/%s with nats: %+v", region, routerName, nats)
194+
op, err := routersService.Patch(project, region, router.Name, patchRouter).Do()
195+
if err != nil {
196+
return fmt.Errorf("Error patching router %s/%s: %s", region, routerName, err)
197+
}
198+
d.SetId(fmt.Sprintf("%s/%s/%s", region, routerName, natName))
199+
err = computeBetaOperationWaitTime(config.clientCompute, op, project, "Patching router", int(d.Timeout(schema.TimeoutCreate).Minutes()))
200+
if err != nil {
201+
d.SetId("")
202+
return fmt.Errorf("Error waiting to patch router %s/%s: %s", region, routerName, err)
203+
}
204+
205+
return resourceComputeRouterNatRead(d, meta)
206+
}
207+
208+
func resourceComputeRouterNatRead(d *schema.ResourceData, meta interface{}) error {
209+
210+
config := meta.(*Config)
211+
212+
region, err := getRegion(d, config)
213+
if err != nil {
214+
return err
215+
}
216+
217+
project, err := getProject(d, config)
218+
if err != nil {
219+
return err
220+
}
221+
222+
routerName := d.Get("router").(string)
223+
natName := d.Get("name").(string)
224+
225+
routersService := config.clientComputeBeta.Routers
226+
router, err := routersService.Get(project, region, routerName).Do()
227+
if err != nil {
228+
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
229+
log.Printf("[WARN] Removing router nat %s because its router %s/%s is gone", natName, region, routerName)
230+
d.SetId("")
231+
232+
return nil
233+
}
234+
235+
return fmt.Errorf("Error Reading router %s/%s: %s", region, routerName, err)
236+
}
237+
238+
for _, nat := range router.Nats {
239+
240+
if nat.Name == natName {
241+
d.SetId(fmt.Sprintf("%s/%s/%s", region, routerName, natName))
242+
d.Set("nat_ip_allocate_option", nat.NatIpAllocateOption)
243+
d.Set("nat_ips", schema.NewSet(schema.HashString, convertStringArrToInterface(convertSelfLinksToV1(nat.NatIps))))
244+
d.Set("source_subnetwork_ip_ranges_to_nat", nat.SourceSubnetworkIpRangesToNat)
245+
d.Set("min_ports_per_vm", nat.MinPortsPerVm)
246+
d.Set("udp_idle_timeout_sec", nat.UdpIdleTimeoutSec)
247+
d.Set("icmp_idle_timeout_sec", nat.IcmpIdleTimeoutSec)
248+
d.Set("tcp_established_idle_timeout_sec", nat.TcpEstablishedIdleTimeoutSec)
249+
d.Set("tcp_transitory_idle_timeout_sec", nat.TcpTransitoryIdleTimeoutSec)
250+
d.Set("region", region)
251+
d.Set("project", project)
252+
253+
if err := d.Set("subnetwork", flattenRouterNatSubnetworkToNatBeta(nat.Subnetworks)); err != nil {
254+
return fmt.Errorf("Error reading router nat: %s", err)
255+
}
256+
257+
return nil
258+
}
259+
}
260+
261+
log.Printf("[WARN] Removing router nat %s/%s/%s because it is gone", region, routerName, natName)
262+
d.SetId("")
263+
return nil
264+
}
265+
266+
func resourceComputeRouterNatDelete(d *schema.ResourceData, meta interface{}) error {
267+
268+
config := meta.(*Config)
269+
270+
region, err := getRegion(d, config)
271+
if err != nil {
272+
return err
273+
}
274+
275+
project, err := getProject(d, config)
276+
if err != nil {
277+
return err
278+
}
279+
280+
routerName := d.Get("router").(string)
281+
natName := d.Get("name").(string)
282+
283+
routerLock := getRouterLockName(region, routerName)
284+
mutexKV.Lock(routerLock)
285+
defer mutexKV.Unlock(routerLock)
286+
287+
routersService := config.clientComputeBeta.Routers
288+
router, err := routersService.Get(project, region, routerName).Do()
289+
if err != nil {
290+
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
291+
log.Printf("[WARN] Removing router nat %s because its router %s/%s is gone", natName, region, routerName)
292+
293+
return nil
294+
}
295+
296+
return fmt.Errorf("Error Reading Router %s: %s", routerName, err)
297+
}
298+
299+
var newNats []*computeBeta.RouterNat = make([]*computeBeta.RouterNat, 0, len(router.Nats))
300+
for _, nat := range router.Nats {
301+
if nat.Name == natName {
302+
continue
303+
} else {
304+
newNats = append(newNats, nat)
305+
}
306+
}
307+
308+
if len(newNats) == len(router.Nats) {
309+
log.Printf("[DEBUG] Router %s/%s had no nat %s already", region, routerName, natName)
310+
d.SetId("")
311+
return nil
312+
}
313+
314+
log.Printf("[INFO] Removing nat %s from router %s/%s", natName, region, routerName)
315+
patchRouter := &computeBeta.Router{
316+
Nats: newNats,
317+
}
318+
319+
if len(newNats) == 0 {
320+
patchRouter.ForceSendFields = append(patchRouter.ForceSendFields, "Nats")
321+
}
322+
323+
log.Printf("[DEBUG] Updating router %s/%s with nats: %+v", region, routerName, newNats)
324+
op, err := routersService.Patch(project, region, router.Name, patchRouter).Do()
325+
if err != nil {
326+
return fmt.Errorf("Error patching router %s/%s: %s", region, routerName, err)
327+
}
328+
329+
err = computeBetaOperationWaitTime(config.clientCompute, op, project, "Patching router", int(d.Timeout(schema.TimeoutDelete).Minutes()))
330+
if err != nil {
331+
return fmt.Errorf("Error waiting to patch router %s/%s: %s", region, routerName, err)
332+
}
333+
334+
d.SetId("")
335+
return nil
336+
}
337+
338+
func resourceComputeRouterNatImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
339+
parts := strings.Split(d.Id(), "/")
340+
if len(parts) != 3 {
341+
return nil, fmt.Errorf("Invalid router nat specifier. Expecting {region}/{router}/{nat}")
342+
}
343+
344+
d.Set("region", parts[0])
345+
d.Set("router", parts[1])
346+
d.Set("name", parts[2])
347+
348+
return []*schema.ResourceData{d}, nil
349+
}
350+
351+
func expandSubnetworks(subnetworks []interface{}) []*computeBeta.RouterNatSubnetworkToNat {
352+
result := make([]*computeBeta.RouterNatSubnetworkToNat, 0, len(subnetworks))
353+
354+
for _, subnetwork := range subnetworks {
355+
snm := subnetwork.(map[string]interface{})
356+
subnetworkToNat := computeBeta.RouterNatSubnetworkToNat{
357+
Name: snm["name"].(string),
358+
SourceIpRangesToNat: convertStringSet(snm["source_ip_ranges_to_nat"].(*schema.Set)),
359+
}
360+
if v, ok := snm["secondary_ip_range_names"]; ok {
361+
subnetworkToNat.SecondaryIpRangeNames = convertStringSet(v.(*schema.Set))
362+
}
363+
result = append(result, &subnetworkToNat)
364+
}
365+
366+
return result
367+
}
368+
369+
func flattenRouterNatSubnetworkToNatBeta(subnetworksToNat []*computeBeta.RouterNatSubnetworkToNat) []map[string]interface{} {
370+
result := make([]map[string]interface{}, 0, len(subnetworksToNat))
371+
for _, subnetworkToNat := range subnetworksToNat {
372+
stnMap := make(map[string]interface{})
373+
stnMap["name"] = ConvertSelfLinkToV1(subnetworkToNat.Name)
374+
stnMap["source_ip_ranges_to_nat"] = schema.NewSet(schema.HashString, convertStringArrToInterface(subnetworkToNat.SourceIpRangesToNat))
375+
stnMap["secondary_ip_range_names"] = schema.NewSet(schema.HashString, convertStringArrToInterface(subnetworkToNat.SecondaryIpRangeNames))
376+
result = append(result, stnMap)
377+
}
378+
return result
379+
}
380+
381+
func convertSelfLinksToV1(selfLinks []string) []string {
382+
result := make([]string, 0, len(selfLinks))
383+
for _, selfLink := range selfLinks {
384+
result = append(result, ConvertSelfLinkToV1(selfLink))
385+
}
386+
return result
387+
}

0 commit comments

Comments
 (0)