forked from hashicorp/terraform-provider-google
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for shared VPC (hashicorp#572)
* Add VPC host project resource * Add VPC service project resource * Add combined acceptance test for shared VPC * Add docs for shared VPC * Increase deadline for project services operation
- Loading branch information
Showing
8 changed files
with
429 additions
and
1 deletion.
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,80 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func resourceComputeSharedVpcHostProject() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceComputeSharedVpcHostProjectCreate, | ||
Read: resourceComputeSharedVpcHostProjectRead, | ||
Delete: resourceComputeSharedVpcHostProjectDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"project": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceComputeSharedVpcHostProjectCreate(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
|
||
hostProject := d.Get("project").(string) | ||
op, err := config.clientCompute.Projects.EnableXpnHost(hostProject).Do() | ||
if err != nil { | ||
return fmt.Errorf("Error enabling Shared VPC Host %q: %s", hostProject, err) | ||
} | ||
|
||
d.SetId(hostProject) | ||
|
||
err = computeOperationWait(config, op, hostProject, "Enabling Shared VPC Host") | ||
if err != nil { | ||
d.SetId("") | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceComputeSharedVpcHostProjectRead(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
|
||
hostProject := d.Get("project").(string) | ||
|
||
project, err := config.clientCompute.Projects.Get(hostProject).Do() | ||
if err != nil { | ||
return handleNotFoundError(err, d, fmt.Sprintf("Project data for project %q", hostProject)) | ||
} | ||
|
||
if project.XpnProjectStatus != "HOST" { | ||
log.Printf("[WARN] Removing Shared VPC host resource %q because it's not enabled server-side", hostProject) | ||
d.SetId("") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceComputeSharedVpcHostProjectDelete(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
hostProject := d.Get("project").(string) | ||
|
||
op, err := config.clientCompute.Projects.DisableXpnHost(hostProject).Do() | ||
if err != nil { | ||
return fmt.Errorf("Error disabling Shared VPC Host %q: %s", hostProject, err) | ||
} | ||
|
||
err = computeOperationWait(config, op, hostProject, "Disabling Shared VPC Host") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
d.SetId("") | ||
return nil | ||
} |
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,121 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
|
||
"google.golang.org/api/compute/v1" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"google.golang.org/api/googleapi" | ||
"log" | ||
) | ||
|
||
func resourceComputeSharedVpcServiceProject() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceComputeSharedVpcServiceProjectCreate, | ||
Read: resourceComputeSharedVpcServiceProjectRead, | ||
Delete: resourceComputeSharedVpcServiceProjectDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"host_project": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"service_project": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceComputeSharedVpcServiceProjectCreate(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
|
||
hostProject := d.Get("host_project").(string) | ||
serviceProject := d.Get("service_project").(string) | ||
|
||
req := &compute.ProjectsEnableXpnResourceRequest{ | ||
XpnResource: &compute.XpnResourceId{ | ||
Id: serviceProject, | ||
Type: "PROJECT", | ||
}, | ||
} | ||
op, err := config.clientCompute.Projects.EnableXpnResource(hostProject, req).Do() | ||
if err != nil { | ||
return err | ||
} | ||
if err = computeOperationWait(config, op, hostProject, "Enabling Shared VPC Resource"); err != nil { | ||
return err | ||
} | ||
|
||
d.SetId(fmt.Sprintf("%s/%s", hostProject, serviceProject)) | ||
|
||
return nil | ||
} | ||
|
||
func resourceComputeSharedVpcServiceProjectRead(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
|
||
hostProject := d.Get("host_project").(string) | ||
serviceProject := d.Get("service_project").(string) | ||
|
||
associatedHostProject, err := config.clientCompute.Projects.GetXpnHost(serviceProject).Do() | ||
if err != nil { | ||
log.Printf("[WARN] Removing shared VPC service. The service project is not associated with any host") | ||
|
||
d.SetId("") | ||
return nil | ||
} | ||
|
||
if hostProject != associatedHostProject.Name { | ||
log.Printf("[WARN] Removing shared VPC service. Expected associated host project to be '%s', got '%s'", hostProject, associatedHostProject.Name) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceComputeSharedVpcServiceProjectDelete(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
hostProject := d.Get("host_project").(string) | ||
serviceProject := d.Get("service_project").(string) | ||
|
||
if err := disableXpnResource(config, hostProject, serviceProject); err != nil { | ||
// Don't fail if the service project is already disabled. | ||
if !isDisabledXpnResourceError(err) { | ||
return fmt.Errorf("Error disabling Shared VPC Resource %q: %s", serviceProject, err) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func disableXpnResource(config *Config, hostProject, project string) error { | ||
req := &compute.ProjectsDisableXpnResourceRequest{ | ||
XpnResource: &compute.XpnResourceId{ | ||
Id: project, | ||
Type: "PROJECT", | ||
}, | ||
} | ||
op, err := config.clientCompute.Projects.DisableXpnResource(hostProject, req).Do() | ||
if err != nil { | ||
return err | ||
} | ||
if err = computeOperationWait(config, op, hostProject, "Disabling Shared VPC Resource"); err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func isDisabledXpnResourceError(err error) bool { | ||
if gerr, ok := err.(*googleapi.Error); ok { | ||
if gerr.Code == 400 && len(gerr.Errors) > 0 && gerr.Errors[0].Reason == "invalidResourceUsage" { | ||
return true | ||
} | ||
} | ||
return false | ||
} |
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,147 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/acctest" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
"os" | ||
) | ||
|
||
func TestAccComputeSharedVpc_basic(t *testing.T) { | ||
skipIfEnvNotSet(t, "GOOGLE_ORG", "GOOGLE_BILLING_ACCOUNT") | ||
billingId := os.Getenv("GOOGLE_BILLING_ACCOUNT") | ||
|
||
hostProject := "xpn-host-" + acctest.RandString(10) | ||
serviceProject := "xpn-service-" + acctest.RandString(10) | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: testAccComputeSharedVpc_basic(hostProject, serviceProject, org, billingId), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckComputeSharedVpcHostProject(hostProject, true), | ||
testAccCheckComputeSharedVpcServiceProject(hostProject, serviceProject, true), | ||
), | ||
}, | ||
// Use a separate TestStep rather than a CheckDestroy because we need the project to still exist. | ||
resource.TestStep{ | ||
Config: testAccComputeSharedVpc_disabled(hostProject, serviceProject, org, billingId), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckComputeSharedVpcHostProject(hostProject, false), | ||
testAccCheckComputeSharedVpcServiceProject(hostProject, serviceProject, false), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckComputeSharedVpcHostProject(hostProject string, enabled bool) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
config := testAccProvider.Meta().(*Config) | ||
|
||
found, err := config.clientCompute.Projects.Get(hostProject).Do() | ||
if err != nil { | ||
return fmt.Errorf("Error reading project %s: %s", hostProject, err) | ||
} | ||
|
||
if found.Name != hostProject { | ||
return fmt.Errorf("Project %s not found", hostProject) | ||
} | ||
|
||
if enabled != (found.XpnProjectStatus == "HOST") { | ||
return fmt.Errorf("Project %q shared VPC status was not expected, got %q", hostProject, found.XpnProjectStatus) | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccCheckComputeSharedVpcServiceProject(hostProject, serviceProject string, enabled bool) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
config := testAccProvider.Meta().(*Config) | ||
serviceHostProject, err := config.clientCompute.Projects.GetXpnHost(serviceProject).Do() | ||
if err != nil { | ||
if enabled { | ||
return fmt.Errorf("Expected service project to be enabled.") | ||
} | ||
return nil | ||
} | ||
|
||
if enabled != (serviceHostProject.Name == hostProject) { | ||
return fmt.Errorf("Wrong host project for the given service project. Expected '%s', got '%s'", hostProject, serviceHostProject.Name) | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccComputeSharedVpc_basic(hostProject, serviceProject, org, billing string) string { | ||
return fmt.Sprintf(` | ||
resource "google_project" "host" { | ||
project_id = "%s" | ||
name = "%s" | ||
org_id = "%s" | ||
billing_account = "%s" | ||
} | ||
resource "google_project" "service" { | ||
project_id = "%s" | ||
name = "%s" | ||
org_id = "%s" | ||
billing_account = "%s" | ||
} | ||
resource "google_project_services" "host" { | ||
project = "${google_project.host.project_id}" | ||
services = ["compute.googleapis.com"] | ||
} | ||
resource "google_project_services" "service" { | ||
project = "${google_project.service.project_id}" | ||
services = ["compute.googleapis.com"] | ||
} | ||
resource "google_compute_shared_vpc_host_project" "host" { | ||
project = "${google_project.host.project_id}" | ||
depends_on = ["google_project_services.host"] | ||
} | ||
resource "google_compute_shared_vpc_service_project" "service" { | ||
host_project = "${google_project.host.project_id}" | ||
service_project = "${google_project.service.project_id}" | ||
depends_on = ["google_compute_shared_vpc_host_project.host", "google_project_services.service"] | ||
}`, hostProject, hostProject, org, billing, serviceProject, serviceProject, org, billing) | ||
} | ||
|
||
func testAccComputeSharedVpc_disabled(hostProject, serviceProject, org, billing string) string { | ||
return fmt.Sprintf(` | ||
resource "google_project" "host" { | ||
project_id = "%s" | ||
name = "%s" | ||
org_id = "%s" | ||
billing_account = "%s" | ||
} | ||
resource "google_project" "service" { | ||
project_id = "%s" | ||
name = "%s" | ||
org_id = "%s" | ||
billing_account = "%s" | ||
} | ||
resource "google_project_services" "host" { | ||
project = "${google_project.host.project_id}" | ||
services = ["compute.googleapis.com"] | ||
} | ||
resource "google_project_services" "service" { | ||
project = "${google_project.service.project_id}" | ||
services = ["compute.googleapis.com"] | ||
} | ||
`, hostProject, hostProject, org, billing, serviceProject, serviceProject, org, billing) | ||
} |
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.