-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new resource
google_compute_network_peering
- Loading branch information
Showing
5 changed files
with
431 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
"google.golang.org/api/compute/v1" | ||
"google.golang.org/api/googleapi" | ||
"log" | ||
"regexp" | ||
) | ||
|
||
const peerNetworkLinkRegex = "projects/(" + ProjectRegex + ")/global/networks/((?:[a-z](?:[-a-z0-9]*[a-z0-9])?))$" | ||
|
||
func resourceComputeNetworkPeering() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceComputeNetworkPeeringCreate, | ||
Read: resourceComputeNetworkPeeringRead, | ||
Delete: resourceComputeNetworkPeeringDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validateGCPName, | ||
}, | ||
"network": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validateRegexp(peerNetworkLinkRegex), | ||
DiffSuppressFunc: peerNetworkLinkDiffSuppress, | ||
}, | ||
"peer_network": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validateRegexp(peerNetworkLinkRegex), | ||
DiffSuppressFunc: peerNetworkLinkDiffSuppress, | ||
}, | ||
"auto_create_routes": &schema.Schema{ | ||
Type: schema.TypeBool, | ||
ForceNew: true, | ||
Optional: true, | ||
Default: true, | ||
}, | ||
"state": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"state_details": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceComputeNetworkPeeringCreate(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
|
||
err := addPeering(config, d) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
peeringName := d.Get("name").(string) | ||
networkName := getNameFromNetworkLink(d.Get("network").(string)) | ||
|
||
d.SetId(fmt.Sprintf("%s/%s", networkName, peeringName)) | ||
|
||
return resourceComputeNetworkPeeringRead(d, meta) | ||
} | ||
|
||
func resourceComputeNetworkPeeringRead(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
|
||
project, err := getProject(d, config) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
peeringName := d.Get("name").(string) | ||
networkLink := d.Get("network").(string) | ||
networkName := getNameFromNetworkLink(networkLink) | ||
|
||
network, err := config.clientCompute.Networks.Get(project, networkName).Do() | ||
if err != nil { | ||
return handleNotFoundError(err, d, fmt.Sprintf("Network %q", networkName)) | ||
} | ||
|
||
peering := findPeeringFromNetwork(network, peeringName) | ||
if peering == nil { | ||
log.Printf("[WARN] Removing network peering %s from network %s because it's gone", peeringName, networkName) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
// No need to set the `name` and `network` fields. We use both of them to find the peering. | ||
// If they change on GCP, we wouldn't have been able to find the peering in the first place. | ||
d.Set("peer_network", peering.Network) | ||
d.Set("auto_create_routes", peering.AutoCreateRoutes) | ||
d.Set("state", peering.State) | ||
d.Set("state_details", peering.StateDetails) | ||
|
||
return nil | ||
} | ||
|
||
func resourceComputeNetworkPeeringDelete(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
|
||
// Remove the `network` to `peer_network` peering | ||
err := removePeering(config, d) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func findPeeringFromNetwork(network *compute.Network, peeringName string) *compute.NetworkPeering { | ||
for _, p := range network.Peerings { | ||
if p.Name == peeringName { | ||
return p | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func addPeering(config *Config, d *schema.ResourceData) error { | ||
project, err := getProject(d, config) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
name := d.Get("name").(string) | ||
networkLink := d.Get("network").(string) | ||
peerNetworkLink := d.Get("peer_network").(string) | ||
autoCreateRoutes := d.Get("auto_create_routes").(bool) | ||
networkName := getNameFromNetworkLink(networkLink) | ||
|
||
request := &compute.NetworksAddPeeringRequest{ | ||
Name: name, | ||
PeerNetwork: peerNetworkLink, | ||
AutoCreateRoutes: autoCreateRoutes, | ||
} | ||
|
||
addOp, err := config.clientCompute.Networks.AddPeering(project, networkName, request).Do() | ||
if err != nil { | ||
return fmt.Errorf("Error adding network peering: %s", err) | ||
} | ||
|
||
err = computeOperationWait(config, addOp, project, "Adding Network Peering") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func removePeering(config *Config, d *schema.ResourceData) error { | ||
project, err := getProject(d, config) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
name := d.Get("name").(string) | ||
networkLink := d.Get("network").(string) | ||
peerNetworkLink := d.Get("peer_network").(string) | ||
networkName := getNameFromNetworkLink(networkLink) | ||
peerNetworkName := getNameFromNetworkLink(peerNetworkLink) | ||
|
||
request := &compute.NetworksRemovePeeringRequest{ | ||
Name: name, | ||
} | ||
|
||
// Only one delete operation at a time can be performed inside any peered VPCs. | ||
peeringLockName := getNetworkPeeringLockName(networkName, peerNetworkName) | ||
mutexKV.Lock(peeringLockName) | ||
defer mutexKV.Unlock(peeringLockName) | ||
|
||
removeOp, err := config.clientCompute.Networks.RemovePeering(project, networkName, request).Do() | ||
if err != nil { | ||
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { | ||
log.Printf("[WARN] Peering `%s` already removed from network `%s`", name, networkName) | ||
} else { | ||
return fmt.Errorf("Error removing peering `%s` from network `%s`: %s", name, networkName, err) | ||
} | ||
} else { | ||
err = computeOperationWait(config, removeOp, project, "Removing Network Peering") | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func getNameFromNetworkLink(network string) string { | ||
r := regexp.MustCompile(peerNetworkLinkRegex) | ||
|
||
m := r.FindStringSubmatch(network) | ||
return m[2] | ||
} | ||
|
||
func peerNetworkLinkDiffSuppress(k, old, new string, d *schema.ResourceData) bool { | ||
r := regexp.MustCompile(peerNetworkLinkRegex) | ||
|
||
m := r.FindStringSubmatch(old) | ||
if len(m) != 3 { | ||
return false | ||
} | ||
oldProject, oldPeeringNetworkName := m[1], m[2] | ||
|
||
m = r.FindStringSubmatch(new) | ||
if len(m) != 3 { | ||
return false | ||
} | ||
newProject, newPeeringNetworkName := m[1], m[2] | ||
|
||
if oldProject == newProject && oldPeeringNetworkName == newPeeringNetworkName { | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
func getNetworkPeeringLockName(networkName, peerNetworkName string) string { | ||
return fmt.Sprintf("network_peering/%s/%s") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
"github.com/hashicorp/terraform/helper/acctest" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
"google.golang.org/api/compute/v1" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func TestAccComputeNetworkPeering_basic(t *testing.T) { | ||
var peering compute.NetworkPeering | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccComputeNetworkPeeringDestroy, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: testAccComputeNetworkPeering_basic, | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckComputeNetworkPeeringExist("google_compute_network_peering.foo", &peering), | ||
testAccCheckComputeNetworkPeeringAutoCreateRoutes(true, &peering), | ||
testAccCheckComputeNetworkPeeringExist("google_compute_network_peering.bar", &peering), | ||
testAccCheckComputeNetworkPeeringAutoCreateRoutes(true, &peering), | ||
), | ||
}, | ||
}, | ||
}) | ||
|
||
} | ||
|
||
func testAccComputeNetworkPeeringDestroy(s *terraform.State) error { | ||
config := testAccProvider.Meta().(*Config) | ||
|
||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "google_compute_network_peering" { | ||
continue | ||
} | ||
|
||
_, err := config.clientCompute.Networks.Get( | ||
config.Project, rs.Primary.ID).Do() | ||
if err == nil { | ||
return fmt.Errorf("Network peering still exists") | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func testAccCheckComputeNetworkPeeringExist(n string, peering *compute.NetworkPeering) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
rs, ok := s.RootModule().Resources[n] | ||
if !ok { | ||
return fmt.Errorf("Not found: %s", n) | ||
} | ||
|
||
if rs.Primary.ID == "" { | ||
return fmt.Errorf("No ID is set") | ||
} | ||
|
||
config := testAccProvider.Meta().(*Config) | ||
|
||
parts := strings.Split(rs.Primary.ID, "/") | ||
if len(parts) != 2 { | ||
return fmt.Errorf("Invalid network peering identifier: %s", rs.Primary.ID) | ||
} | ||
|
||
networkName, peeringName := parts[0], parts[1] | ||
|
||
network, err := config.clientCompute.Networks.Get(config.Project, networkName).Do() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
found := findPeeringFromNetwork(network, peeringName) | ||
if found == nil { | ||
return fmt.Errorf("Network peering '%s' not found in network '%s'", peeringName, network.Name) | ||
} | ||
*peering = *found | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccCheckComputeNetworkPeeringAutoCreateRoutes(v bool, peering *compute.NetworkPeering) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
if peering.AutoCreateRoutes != v { | ||
return fmt.Errorf("should AutoCreateRoutes set to %t", v) | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
var testAccComputeNetworkPeering_basic = fmt.Sprintf(` | ||
resource "google_compute_network" "network1" { | ||
name = "network-test-1-%s" | ||
auto_create_subnetworks = false | ||
} | ||
resource "google_compute_network" "network2" { | ||
name = "network-test-2-%s" | ||
auto_create_subnetworks = false | ||
} | ||
resource "google_compute_network_peering" "foo" { | ||
name = "peering-test-1-%s" | ||
network = "${google_compute_network.network1.self_link}" | ||
peer_network = "${google_compute_network.network2.self_link}" | ||
} | ||
resource "google_compute_network_peering" "bar" { | ||
name = "peering-test-2-%s" | ||
auto_create_routes = true | ||
network = "${google_compute_network.network2.self_link}" | ||
peer_network = "${google_compute_network.network1.self_link}" | ||
} | ||
`, acctest.RandString(10), acctest.RandString(10), acctest.RandString(10), acctest.RandString(10)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.