Skip to content

Commit 4441908

Browse files
committed
Add new resource google_compute_network_peering
1 parent 232cb87 commit 4441908

5 files changed

+429
-0
lines changed

google/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ func Provider() terraform.ResourceProvider {
8686
"google_compute_instance_group_manager": resourceComputeInstanceGroupManager(),
8787
"google_compute_instance_template": resourceComputeInstanceTemplate(),
8888
"google_compute_network": resourceComputeNetwork(),
89+
"google_compute_network_peering": resourceComputeNetworkPeering(),
8990
"google_compute_project_metadata": resourceComputeProjectMetadata(),
9091
"google_compute_region_backend_service": resourceComputeRegionBackendService(),
9192
"google_compute_route": resourceComputeRoute(),
+229
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
package google
2+
3+
import (
4+
"fmt"
5+
"github.com/hashicorp/terraform/helper/schema"
6+
"google.golang.org/api/compute/v1"
7+
"google.golang.org/api/googleapi"
8+
"log"
9+
"regexp"
10+
)
11+
12+
const peerNetworkLinkRegex = "projects/(" + ProjectRegex + ")/global/networks/((?:[a-z](?:[-a-z0-9]*[a-z0-9])?))$"
13+
14+
func resourceComputeNetworkPeering() *schema.Resource {
15+
return &schema.Resource{
16+
Create: resourceComputeNetworkPeeringCreate,
17+
Read: resourceComputeNetworkPeeringRead,
18+
Delete: resourceComputeNetworkPeeringDelete,
19+
20+
Schema: map[string]*schema.Schema{
21+
"name": &schema.Schema{
22+
Type: schema.TypeString,
23+
Required: true,
24+
ForceNew: true,
25+
ValidateFunc: validateGCPName,
26+
},
27+
"network": &schema.Schema{
28+
Type: schema.TypeString,
29+
Required: true,
30+
ForceNew: true,
31+
ValidateFunc: validateRegexp(peerNetworkLinkRegex),
32+
DiffSuppressFunc: peerNetworkLinkDiffSuppress,
33+
},
34+
"peer_network": &schema.Schema{
35+
Type: schema.TypeString,
36+
Required: true,
37+
ForceNew: true,
38+
ValidateFunc: validateRegexp(peerNetworkLinkRegex),
39+
DiffSuppressFunc: peerNetworkLinkDiffSuppress,
40+
},
41+
"auto_create_routes": &schema.Schema{
42+
Type: schema.TypeBool,
43+
ForceNew: true,
44+
Optional: true,
45+
Default: true,
46+
},
47+
"state": &schema.Schema{
48+
Type: schema.TypeString,
49+
Computed: true,
50+
},
51+
"state_details": &schema.Schema{
52+
Type: schema.TypeString,
53+
Computed: true,
54+
},
55+
},
56+
}
57+
}
58+
59+
func resourceComputeNetworkPeeringCreate(d *schema.ResourceData, meta interface{}) error {
60+
config := meta.(*Config)
61+
62+
err := addPeering(config, d)
63+
if err != nil {
64+
return err
65+
}
66+
67+
peeringName := d.Get("name").(string)
68+
networkName := getNameFromNetworkLink(d.Get("network").(string))
69+
70+
d.SetId(fmt.Sprintf("%s/%s", networkName, peeringName))
71+
72+
return resourceComputeNetworkPeeringRead(d, meta)
73+
}
74+
75+
func resourceComputeNetworkPeeringRead(d *schema.ResourceData, meta interface{}) error {
76+
config := meta.(*Config)
77+
78+
project, err := getProject(d, config)
79+
if err != nil {
80+
return err
81+
}
82+
83+
peeringName := d.Get("name").(string)
84+
networkLink := d.Get("network").(string)
85+
networkName := getNameFromNetworkLink(networkLink)
86+
87+
network, err := config.clientCompute.Networks.Get(project, networkName).Do()
88+
if err != nil {
89+
return handleNotFoundError(err, d, fmt.Sprintf("Network %q", networkName))
90+
}
91+
92+
peering := findPeeringFromNetwork(network, peeringName)
93+
if peering == nil {
94+
log.Printf("[WARN] Removing network peering %s from network %s because it's gone", peeringName, networkName)
95+
d.SetId("")
96+
return nil
97+
}
98+
99+
// No need to set the `name` and `network` fields. We use both of them to find the peering.
100+
// If they change on GCP, we wouldn't have been able to find the peering in the first place.
101+
d.Set("peer_network", peering.Network)
102+
d.Set("auto_create_routes", peering.AutoCreateRoutes)
103+
d.Set("state", peering.State)
104+
d.Set("state_details", peering.StateDetails)
105+
106+
return nil
107+
}
108+
109+
func resourceComputeNetworkPeeringDelete(d *schema.ResourceData, meta interface{}) error {
110+
config := meta.(*Config)
111+
112+
// Remove the `network` to `peer_network` peering
113+
err := removePeering(config, d)
114+
if err != nil {
115+
return err
116+
}
117+
118+
return nil
119+
}
120+
121+
func findPeeringFromNetwork(network *compute.Network, peeringName string) *compute.NetworkPeering {
122+
for _, p := range network.Peerings {
123+
if p.Name == peeringName {
124+
return p
125+
}
126+
}
127+
return nil
128+
}
129+
130+
func addPeering(config *Config, d *schema.ResourceData) error {
131+
project, err := getProject(d, config)
132+
if err != nil {
133+
return err
134+
}
135+
136+
name := d.Get("name").(string)
137+
networkLink := d.Get("network").(string)
138+
peerNetworkLink := d.Get("peer_network").(string)
139+
autoCreateRoutes := d.Get("auto_create_routes").(bool)
140+
networkName := getNameFromNetworkLink(networkLink)
141+
142+
request := &compute.NetworksAddPeeringRequest{
143+
Name: name,
144+
PeerNetwork: peerNetworkLink,
145+
AutoCreateRoutes: autoCreateRoutes,
146+
}
147+
148+
addOp, err := config.clientCompute.Networks.AddPeering(project, networkName, request).Do()
149+
if err != nil {
150+
return fmt.Errorf("Error adding network peering: %s", err)
151+
}
152+
153+
err = computeOperationWait(config, addOp, project, "Adding Network Peering")
154+
if err != nil {
155+
return err
156+
}
157+
158+
return nil
159+
}
160+
161+
func removePeering(config *Config, d *schema.ResourceData) error {
162+
project, err := getProject(d, config)
163+
if err != nil {
164+
return err
165+
}
166+
167+
name := d.Get("name").(string)
168+
networkLink := d.Get("network").(string)
169+
peerNetworkLink := d.Get("peer_network").(string)
170+
networkName := getNameFromNetworkLink(networkLink)
171+
peerNetworkName := getNameFromNetworkLink(peerNetworkLink)
172+
173+
request := &compute.NetworksRemovePeeringRequest{
174+
Name: name,
175+
}
176+
177+
// Only one delete peering operation at a time can be performed inside any peered VPCs.
178+
peeringLockName := getNetworkPeeringLockName(networkName, peerNetworkName)
179+
mutexKV.Lock(peeringLockName)
180+
defer mutexKV.Unlock(peeringLockName)
181+
182+
removeOp, err := config.clientCompute.Networks.RemovePeering(project, networkName, request).Do()
183+
if err != nil {
184+
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
185+
log.Printf("[WARN] Peering `%s` already removed from network `%s`", name, networkName)
186+
} else {
187+
return fmt.Errorf("Error removing peering `%s` from network `%s`: %s", name, networkName, err)
188+
}
189+
} else {
190+
err = computeOperationWait(config, removeOp, project, "Removing Network Peering")
191+
if err != nil {
192+
return err
193+
}
194+
}
195+
196+
return nil
197+
}
198+
199+
func getNameFromNetworkLink(network string) string {
200+
r := regexp.MustCompile(peerNetworkLinkRegex)
201+
202+
m := r.FindStringSubmatch(network)
203+
return m[2]
204+
}
205+
206+
func peerNetworkLinkDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
207+
r := regexp.MustCompile(peerNetworkLinkRegex)
208+
209+
m := r.FindStringSubmatch(old)
210+
if len(m) != 3 {
211+
return false
212+
}
213+
oldProject, oldPeeringNetworkName := m[1], m[2]
214+
215+
m = r.FindStringSubmatch(new)
216+
if len(m) != 3 {
217+
return false
218+
}
219+
newProject, newPeeringNetworkName := m[1], m[2]
220+
221+
if oldProject == newProject && oldPeeringNetworkName == newPeeringNetworkName {
222+
return true
223+
}
224+
return false
225+
}
226+
227+
func getNetworkPeeringLockName(networkName, peerNetworkName string) string {
228+
return fmt.Sprintf("network_peering/%s/%s", networkName, peerNetworkName)
229+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package google
2+
3+
import (
4+
"fmt"
5+
"github.com/hashicorp/terraform/helper/acctest"
6+
"github.com/hashicorp/terraform/helper/resource"
7+
"github.com/hashicorp/terraform/terraform"
8+
"google.golang.org/api/compute/v1"
9+
"strings"
10+
"testing"
11+
)
12+
13+
func TestAccComputeNetworkPeering_basic(t *testing.T) {
14+
var peering compute.NetworkPeering
15+
16+
resource.Test(t, resource.TestCase{
17+
PreCheck: func() { testAccPreCheck(t) },
18+
Providers: testAccProviders,
19+
CheckDestroy: testAccComputeNetworkPeeringDestroy,
20+
Steps: []resource.TestStep{
21+
resource.TestStep{
22+
Config: testAccComputeNetworkPeering_basic,
23+
Check: resource.ComposeTestCheckFunc(
24+
testAccCheckComputeNetworkPeeringExist("google_compute_network_peering.foo", &peering),
25+
testAccCheckComputeNetworkPeeringAutoCreateRoutes(true, &peering),
26+
testAccCheckComputeNetworkPeeringExist("google_compute_network_peering.bar", &peering),
27+
testAccCheckComputeNetworkPeeringAutoCreateRoutes(true, &peering),
28+
),
29+
},
30+
},
31+
})
32+
33+
}
34+
35+
func testAccComputeNetworkPeeringDestroy(s *terraform.State) error {
36+
config := testAccProvider.Meta().(*Config)
37+
38+
for _, rs := range s.RootModule().Resources {
39+
if rs.Type != "google_compute_network_peering" {
40+
continue
41+
}
42+
43+
_, err := config.clientCompute.Networks.Get(
44+
config.Project, rs.Primary.ID).Do()
45+
if err == nil {
46+
return fmt.Errorf("Network peering still exists")
47+
}
48+
}
49+
50+
return nil
51+
}
52+
53+
func testAccCheckComputeNetworkPeeringExist(n string, peering *compute.NetworkPeering) resource.TestCheckFunc {
54+
return func(s *terraform.State) error {
55+
rs, ok := s.RootModule().Resources[n]
56+
if !ok {
57+
return fmt.Errorf("Not found: %s", n)
58+
}
59+
60+
if rs.Primary.ID == "" {
61+
return fmt.Errorf("No ID is set")
62+
}
63+
64+
config := testAccProvider.Meta().(*Config)
65+
66+
parts := strings.Split(rs.Primary.ID, "/")
67+
if len(parts) != 2 {
68+
return fmt.Errorf("Invalid network peering identifier: %s", rs.Primary.ID)
69+
}
70+
71+
networkName, peeringName := parts[0], parts[1]
72+
73+
network, err := config.clientCompute.Networks.Get(config.Project, networkName).Do()
74+
if err != nil {
75+
return err
76+
}
77+
78+
found := findPeeringFromNetwork(network, peeringName)
79+
if found == nil {
80+
return fmt.Errorf("Network peering '%s' not found in network '%s'", peeringName, network.Name)
81+
}
82+
*peering = *found
83+
84+
return nil
85+
}
86+
}
87+
88+
func testAccCheckComputeNetworkPeeringAutoCreateRoutes(v bool, peering *compute.NetworkPeering) resource.TestCheckFunc {
89+
return func(s *terraform.State) error {
90+
if peering.AutoCreateRoutes != v {
91+
return fmt.Errorf("should AutoCreateRoutes set to %t", v)
92+
}
93+
94+
return nil
95+
}
96+
}
97+
98+
var testAccComputeNetworkPeering_basic = fmt.Sprintf(`
99+
resource "google_compute_network" "network1" {
100+
name = "network-test-1-%s"
101+
auto_create_subnetworks = false
102+
}
103+
104+
resource "google_compute_network" "network2" {
105+
name = "network-test-2-%s"
106+
auto_create_subnetworks = false
107+
}
108+
109+
resource "google_compute_network_peering" "foo" {
110+
name = "peering-test-1-%s"
111+
network = "${google_compute_network.network1.self_link}"
112+
peer_network = "${google_compute_network.network2.self_link}"
113+
}
114+
115+
resource "google_compute_network_peering" "bar" {
116+
name = "peering-test-2-%s"
117+
auto_create_routes = true
118+
network = "${google_compute_network.network2.self_link}"
119+
peer_network = "${google_compute_network.network1.self_link}"
120+
}
121+
`, acctest.RandString(10), acctest.RandString(10), acctest.RandString(10), acctest.RandString(10))

google/validation.go

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"regexp"
77
)
88

9+
const ProjectRegex = "(?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?))"
10+
911
func validateGCPName(v interface{}, k string) (ws []string, errors []error) {
1012
re := `^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$`
1113
return validateRegexp(re)(v, k)

0 commit comments

Comments
 (0)