Skip to content

Commit eda53c2

Browse files
rosboaadidenko
authored andcommitted
Add new resource google_compute_network_peering (hashicorp#259)
1 parent c68d2cc commit eda53c2

5 files changed

+380
-0
lines changed

google/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ func Provider() terraform.ResourceProvider {
8787
"google_compute_instance_group_manager": resourceComputeInstanceGroupManager(),
8888
"google_compute_instance_template": resourceComputeInstanceTemplate(),
8989
"google_compute_network": resourceComputeNetwork(),
90+
"google_compute_network_peering": resourceComputeNetworkPeering(),
9091
"google_compute_project_metadata": resourceComputeProjectMetadata(),
9192
"google_compute_region_backend_service": resourceComputeRegionBackendService(),
9293
"google_compute_route": resourceComputeRoute(),
+189
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package google
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"regexp"
7+
"sort"
8+
9+
"github.com/hashicorp/terraform/helper/schema"
10+
"google.golang.org/api/compute/v1"
11+
"google.golang.org/api/googleapi"
12+
)
13+
14+
const peerNetworkLinkRegex = "projects/(" + ProjectRegex + ")/global/networks/((?:[a-z](?:[-a-z0-9]*[a-z0-9])?))$"
15+
16+
func resourceComputeNetworkPeering() *schema.Resource {
17+
return &schema.Resource{
18+
Create: resourceComputeNetworkPeeringCreate,
19+
Read: resourceComputeNetworkPeeringRead,
20+
Delete: resourceComputeNetworkPeeringDelete,
21+
22+
Schema: map[string]*schema.Schema{
23+
"name": &schema.Schema{
24+
Type: schema.TypeString,
25+
Required: true,
26+
ForceNew: true,
27+
ValidateFunc: validateGCPName,
28+
},
29+
"network": &schema.Schema{
30+
Type: schema.TypeString,
31+
Required: true,
32+
ForceNew: true,
33+
ValidateFunc: validateRegexp(peerNetworkLinkRegex),
34+
DiffSuppressFunc: compareSelfLinkRelativePaths,
35+
},
36+
"peer_network": &schema.Schema{
37+
Type: schema.TypeString,
38+
Required: true,
39+
ForceNew: true,
40+
ValidateFunc: validateRegexp(peerNetworkLinkRegex),
41+
DiffSuppressFunc: compareSelfLinkRelativePaths,
42+
},
43+
"auto_create_routes": &schema.Schema{
44+
Type: schema.TypeBool,
45+
ForceNew: true,
46+
Optional: true,
47+
Default: true,
48+
},
49+
"state": &schema.Schema{
50+
Type: schema.TypeString,
51+
Computed: true,
52+
},
53+
"state_details": &schema.Schema{
54+
Type: schema.TypeString,
55+
Computed: true,
56+
},
57+
},
58+
}
59+
}
60+
61+
func resourceComputeNetworkPeeringCreate(d *schema.ResourceData, meta interface{}) error {
62+
config := meta.(*Config)
63+
64+
project, err := getProject(d, config)
65+
if err != nil {
66+
return err
67+
}
68+
69+
networkLink := d.Get("network").(string)
70+
networkName := getNameFromNetworkLink(networkLink)
71+
72+
request := &compute.NetworksAddPeeringRequest{
73+
Name: d.Get("name").(string),
74+
PeerNetwork: d.Get("peer_network").(string),
75+
AutoCreateRoutes: d.Get("auto_create_routes").(bool),
76+
}
77+
78+
addOp, err := config.clientCompute.Networks.AddPeering(project, networkName, request).Do()
79+
if err != nil {
80+
return fmt.Errorf("Error adding network peering: %s", err)
81+
}
82+
83+
err = computeOperationWait(config, addOp, project, "Adding Network Peering")
84+
if err != nil {
85+
return err
86+
}
87+
88+
d.SetId(fmt.Sprintf("%s/%s", networkName, d.Get("name").(string)))
89+
90+
return resourceComputeNetworkPeeringRead(d, meta)
91+
}
92+
93+
func resourceComputeNetworkPeeringRead(d *schema.ResourceData, meta interface{}) error {
94+
config := meta.(*Config)
95+
96+
project, err := getProject(d, config)
97+
if err != nil {
98+
return err
99+
}
100+
101+
peeringName := d.Get("name").(string)
102+
networkLink := d.Get("network").(string)
103+
networkName := getNameFromNetworkLink(networkLink)
104+
105+
network, err := config.clientCompute.Networks.Get(project, networkName).Do()
106+
if err != nil {
107+
return handleNotFoundError(err, d, fmt.Sprintf("Network %q", networkName))
108+
}
109+
110+
peering := findPeeringFromNetwork(network, peeringName)
111+
if peering == nil {
112+
log.Printf("[WARN] Removing network peering %s from network %s because it's gone", peeringName, networkName)
113+
d.SetId("")
114+
return nil
115+
}
116+
117+
d.Set("peer_network", peering.Network)
118+
d.Set("auto_create_routes", peering.AutoCreateRoutes)
119+
d.Set("state", peering.State)
120+
d.Set("state_details", peering.StateDetails)
121+
122+
return nil
123+
}
124+
125+
func resourceComputeNetworkPeeringDelete(d *schema.ResourceData, meta interface{}) error {
126+
config := meta.(*Config)
127+
128+
// Remove the `network` to `peer_network` peering
129+
project, err := getProject(d, config)
130+
if err != nil {
131+
return err
132+
}
133+
134+
name := d.Get("name").(string)
135+
networkLink := d.Get("network").(string)
136+
peerNetworkLink := d.Get("peer_network").(string)
137+
networkName := getNameFromNetworkLink(networkLink)
138+
peerNetworkName := getNameFromNetworkLink(peerNetworkLink)
139+
140+
request := &compute.NetworksRemovePeeringRequest{
141+
Name: name,
142+
}
143+
144+
// Only one delete peering operation at a time can be performed inside any peered VPCs.
145+
peeringLockName := getNetworkPeeringLockName(networkName, peerNetworkName)
146+
mutexKV.Lock(peeringLockName)
147+
defer mutexKV.Unlock(peeringLockName)
148+
149+
removeOp, err := config.clientCompute.Networks.RemovePeering(project, networkName, request).Do()
150+
if err != nil {
151+
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
152+
log.Printf("[WARN] Peering `%s` already removed from network `%s`", name, networkName)
153+
} else {
154+
return fmt.Errorf("Error removing peering `%s` from network `%s`: %s", name, networkName, err)
155+
}
156+
} else {
157+
err = computeOperationWait(config, removeOp, project, "Removing Network Peering")
158+
if err != nil {
159+
return err
160+
}
161+
}
162+
163+
return nil
164+
}
165+
166+
func findPeeringFromNetwork(network *compute.Network, peeringName string) *compute.NetworkPeering {
167+
for _, p := range network.Peerings {
168+
if p.Name == peeringName {
169+
return p
170+
}
171+
}
172+
return nil
173+
}
174+
175+
func getNameFromNetworkLink(network string) string {
176+
r := regexp.MustCompile(peerNetworkLinkRegex)
177+
178+
m := r.FindStringSubmatch(network)
179+
return m[2]
180+
}
181+
182+
func getNetworkPeeringLockName(networkName, peerNetworkName string) string {
183+
// Whether you delete the peering from network A to B or the one from B to A, they
184+
// cannot happen at the same time.
185+
networks := []string{networkName, peerNetworkName}
186+
sort.Strings(networks)
187+
188+
return fmt.Sprintf("network_peering/%s/%s", networks[0], networks[1])
189+
}
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

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

9+
// Copied from the official Google Cloud auto-generated client.
10+
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])?))"
11+
912
func validateGCPName(v interface{}, k string) (ws []string, errors []error) {
1013
re := `^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$`
1114
return validateRegexp(re)(v, k)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
layout: "google"
3+
page_title: "Google: google_compute_network_peering"
4+
sidebar_current: "docs-google-compute-network-peering"
5+
description: |-
6+
Manages a network peering within GCE.
7+
---
8+
9+
# google\_compute\_network\_peering
10+
11+
Manages a network peering within GCE. For more information see
12+
[the official documentation](https://cloud.google.com/compute/docs/vpc/vpc-peering)
13+
and
14+
[API](https://cloud.google.com/compute/docs/reference/latest/networks).
15+
16+
~> **Note:** Both network must create a peering with each other for the peering to be functional.
17+
18+
~> **Note:** Subnets IP ranges across peered VPC networks cannot overlap.
19+
20+
## Example Usage
21+
22+
```hcl
23+
resource "google_compute_network_peering" "peering1" {
24+
name = "peering1"
25+
network = "${google_compute_network.default.self_link}"
26+
peer_network = "${google_compute_network.other.self_link}"
27+
}
28+
29+
resource "google_compute_network_peering" "peering2" {
30+
name = "peering2"
31+
network = "${google_compute_network.other.self_link}"
32+
peer_network = "${google_compute_network.default.self_link}"
33+
}
34+
35+
resource "google_compute_network" "default" {
36+
name = "foobar"
37+
auto_create_subnetworks = "false"
38+
}
39+
40+
resource "google_compute_network" "other" {
41+
name = "other"
42+
auto_create_subnetworks = "false"
43+
}
44+
```
45+
46+
## Argument Reference
47+
48+
The following arguments are supported:
49+
50+
* `name` - (Required) Name of the peering.
51+
52+
* `network` - (Required) Resource link of the network to add a peering to.
53+
54+
* `peer_network` - (Required) Resource link of the peer network.
55+
56+
* `auto_create_routes` - (Optional) If set to `true`, the routes between the two networks will
57+
be created and managed automatically. Defaults to `true`.
58+
59+
## Attributes Reference
60+
61+
In addition to the arguments listed above, the following computed attributes are
62+
exported:
63+
64+
* `state` - State for the peering.
65+
66+
* `state_details` - Details about the current state of the peering.

0 commit comments

Comments
 (0)