From 2bb9b1c6911208aba3819bb39ef106fdec0a02e1 Mon Sep 17 00:00:00 2001
From: Richard Hsu <reechar@google.com>
Date: Thu, 2 May 2019 21:22:54 +0000
Subject: [PATCH] IAM Policy on subnets is now GA, moving into non-beta
 provider

Signed-off-by: Modular Magician <magic-modules@google.com>
---
 google/iam_compute_subnetwork.go              | 163 +++++++++++-
 google/provider.go                            |   3 +
 .../resource_compute_subnetwork_iam_test.go   | 246 +++++++++++++++++-
 3 files changed, 410 insertions(+), 2 deletions(-)

diff --git a/google/iam_compute_subnetwork.go b/google/iam_compute_subnetwork.go
index 93cfad7a2a0..5508b446951 100644
--- a/google/iam_compute_subnetwork.go
+++ b/google/iam_compute_subnetwork.go
@@ -1,3 +1,164 @@
 package google
 
-// Magic Modules doesn't let us remove files - blank out beta-only common-compile files for now.
+import (
+	"fmt"
+	"strings"
+
+	"github.com/hashicorp/errwrap"
+	"github.com/hashicorp/terraform/helper/schema"
+	"google.golang.org/api/cloudresourcemanager/v1"
+	"google.golang.org/api/compute/v1"
+)
+
+var IamComputeSubnetworkSchema = map[string]*schema.Schema{
+	"subnetwork": {
+		Type:     schema.TypeString,
+		Required: true,
+		ForceNew: true,
+	},
+
+	"project": {
+		Type:     schema.TypeString,
+		Optional: true,
+		Computed: true,
+		ForceNew: true,
+	},
+
+	"region": {
+		Type:     schema.TypeString,
+		Optional: true,
+		Computed: true,
+		ForceNew: true,
+	},
+}
+
+type ComputeSubnetworkIamUpdater struct {
+	project    string
+	region     string
+	resourceId string
+	Config     *Config
+}
+
+func NewComputeSubnetworkIamUpdater(d *schema.ResourceData, config *Config) (ResourceIamUpdater, error) {
+	project, err := getProject(d, config)
+	if err != nil {
+		return nil, err
+	}
+
+	region, err := getRegion(d, config)
+	if err != nil {
+		return nil, err
+	}
+
+	return &ComputeSubnetworkIamUpdater{
+		project:    project,
+		region:     region,
+		resourceId: d.Get("subnetwork").(string),
+		Config:     config,
+	}, nil
+}
+
+func ComputeSubnetworkIdParseFunc(d *schema.ResourceData, config *Config) error {
+	parts := strings.Split(d.Id(), "/")
+	var fv *RegionalFieldValue
+	if len(parts) == 3 {
+		// {project}/{region}/{name} syntax
+		fv = &RegionalFieldValue{
+			Project:      parts[0],
+			Region:       parts[1],
+			Name:         parts[2],
+			resourceType: "subnetworks",
+		}
+	} else if len(parts) == 2 {
+		// /{region}/{name} syntax
+		project, err := getProject(d, config)
+		if err != nil {
+			return err
+		}
+		fv = &RegionalFieldValue{
+			Project:      project,
+			Region:       parts[0],
+			Name:         parts[1],
+			resourceType: "subnetworks",
+		}
+	} else {
+		// We either have a name or a full self link, so use the field helper
+		var err error
+		fv, err = ParseSubnetworkFieldValue(d.Id(), d, config)
+		if err != nil {
+			return err
+		}
+	}
+	d.Set("subnetwork", fv.Name)
+	d.Set("project", fv.Project)
+	d.Set("region", fv.Region)
+
+	// Explicitly set the id so imported resources have the same ID format as non-imported ones.
+	d.SetId(fv.RelativeLink())
+	return nil
+}
+
+func (u *ComputeSubnetworkIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) {
+	p, err := u.Config.clientCompute.Subnetworks.GetIamPolicy(u.project, u.region, u.resourceId).Do()
+
+	if err != nil {
+		return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err)
+	}
+
+	cloudResourcePolicy, err := computeToResourceManagerPolicy(p)
+
+	if err != nil {
+		return nil, errwrap.Wrapf(fmt.Sprintf("Invalid IAM policy for %s: {{err}}", u.DescribeResource()), err)
+	}
+
+	return cloudResourcePolicy, nil
+}
+
+func (u *ComputeSubnetworkIamUpdater) SetResourceIamPolicy(policy *cloudresourcemanager.Policy) error {
+	computePolicy, err := resourceManagerToComputePolicy(policy)
+
+	if err != nil {
+		return errwrap.Wrapf(fmt.Sprintf("Invalid IAM policy for %s: {{err}}", u.DescribeResource()), err)
+	}
+
+	req := &compute.RegionSetPolicyRequest{
+		Policy: computePolicy,
+	}
+	_, err = u.Config.clientCompute.Subnetworks.SetIamPolicy(u.project, u.region, u.resourceId, req).Do()
+
+	if err != nil {
+		return errwrap.Wrapf(fmt.Sprintf("Error setting IAM policy for %s: {{err}}", u.DescribeResource()), err)
+	}
+
+	return nil
+}
+
+func (u *ComputeSubnetworkIamUpdater) GetResourceId() string {
+	return fmt.Sprintf("projects/%s/regions/%s/subnetworks/%s", u.project, u.region, u.resourceId)
+}
+
+func (u *ComputeSubnetworkIamUpdater) GetMutexKey() string {
+	return fmt.Sprintf("iam-compute-subnetwork-%s-%s-%s", u.project, u.region, u.resourceId)
+}
+
+func (u *ComputeSubnetworkIamUpdater) DescribeResource() string {
+	return fmt.Sprintf("Compute Subnetwork %s/%s/%s", u.project, u.region, u.resourceId)
+}
+
+func resourceManagerToComputePolicy(p *cloudresourcemanager.Policy) (*compute.Policy, error) {
+	out := &compute.Policy{}
+	err := Convert(p, out)
+	if err != nil {
+		return nil, errwrap.Wrapf("Cannot convert a resourcemanager policy to a compute policy: {{err}}", err)
+	}
+	return out, nil
+}
+
+func computeToResourceManagerPolicy(p *compute.Policy) (*cloudresourcemanager.Policy, error) {
+	out := &cloudresourcemanager.Policy{}
+	err := Convert(p, out)
+	if err != nil {
+		return nil, errwrap.Wrapf("Cannot convert a compute policy to a resourcemanager policy: {{err}}", err)
+	}
+	return out, nil
+}
diff --git a/google/provider.go b/google/provider.go
index fa4c159b499..8895d9f20ad 100644
--- a/google/provider.go
+++ b/google/provider.go
@@ -184,6 +184,9 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) {
 			"google_compute_security_policy":               resourceComputeSecurityPolicy(),
 			"google_compute_shared_vpc_host_project":       resourceComputeSharedVpcHostProject(),
 			"google_compute_shared_vpc_service_project":    resourceComputeSharedVpcServiceProject(),
+			"google_compute_subnetwork_iam_binding":        ResourceIamBindingWithImport(IamComputeSubnetworkSchema, NewComputeSubnetworkIamUpdater, ComputeSubnetworkIdParseFunc),
+			"google_compute_subnetwork_iam_member":         ResourceIamMemberWithImport(IamComputeSubnetworkSchema, NewComputeSubnetworkIamUpdater, ComputeSubnetworkIdParseFunc),
+			"google_compute_subnetwork_iam_policy":         ResourceIamPolicyWithImport(IamComputeSubnetworkSchema, NewComputeSubnetworkIamUpdater, ComputeSubnetworkIdParseFunc),
 			"google_compute_target_pool":                   resourceComputeTargetPool(),
 			"google_container_cluster":                     resourceContainerCluster(),
 			"google_container_node_pool":                   resourceContainerNodePool(),
diff --git a/google/resource_compute_subnetwork_iam_test.go b/google/resource_compute_subnetwork_iam_test.go
index 93cfad7a2a0..fb25002a367 100644
--- a/google/resource_compute_subnetwork_iam_test.go
+++ b/google/resource_compute_subnetwork_iam_test.go
@@ -1,3 +1,247 @@
 package google
 
-// Magic Modules doesn't let us remove files - blank out beta-only common-compile files for now.
+import (
+	"fmt"
+	"testing"
+
+	"github.com/hashicorp/terraform/helper/acctest"
+	"github.com/hashicorp/terraform/helper/resource"
+)
+
+func TestAccComputeSubnetworkIamBinding(t *testing.T) {
+	t.Parallel()
+
+	account := acctest.RandomWithPrefix("tf-test")
+	role := "roles/compute.networkUser"
+	region := getTestRegionFromEnv()
+	subnetwork := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
+
+	resource.Test(t, resource.TestCase{
+		PreCheck:  func() { testAccPreCheck(t) },
+		Providers: testAccProviders,
+		Steps: []resource.TestStep{
+			{
+				Config: testAccComputeSubnetworkIamBinding_basic(account, region, subnetwork, role),
+			},
+			{
+				ResourceName:      "google_compute_subnetwork_iam_binding.foo",
+				ImportStateId:     fmt.Sprintf("%s/%s %s", region, subnetwork, role),
+				ImportState:       true,
+				ImportStateVerify: true,
+			},
+			{
+				// Test Iam Binding update
+				Config: testAccComputeSubnetworkIamBinding_update(account, region, subnetwork, role),
+			},
+			{
+				ResourceName:      "google_compute_subnetwork_iam_binding.foo",
+				ImportStateId:     fmt.Sprintf("%s/%s %s", region, subnetwork, role),
+				ImportState:       true,
+				ImportStateVerify: true,
+			},
+		},
+	})
+}
+
+func TestAccComputeSubnetworkIamMember(t *testing.T) {
+	t.Parallel()
+
+	project := getTestProjectFromEnv()
+	account := acctest.RandomWithPrefix("tf-test")
+	role := "roles/compute.networkUser"
+	region := getTestRegionFromEnv()
+	subnetwork := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
+
+	resource.Test(t, resource.TestCase{
+		PreCheck:  func() { testAccPreCheck(t) },
+		Providers: testAccProviders,
+		Steps: []resource.TestStep{
+			{
+				// Test Iam Member creation (no update for member, no need to test)
+				Config: testAccComputeSubnetworkIamMember_basic(account, region, subnetwork, role),
+			},
+			{
+				ResourceName:      "google_compute_subnetwork_iam_member.foo",
+				ImportStateId:     fmt.Sprintf("%s/%s %s serviceAccount:%s@%s.iam.gserviceaccount.com", region, subnetwork, role, account, project),
+				ImportState:       true,
+				ImportStateVerify: true,
+			},
+		},
+	})
+}
+
+func TestAccComputeSubnetworkIamPolicy(t *testing.T) {
+	t.Parallel()
+
+	project := getTestProjectFromEnv()
+	account := acctest.RandomWithPrefix("tf-test")
+	role := "roles/compute.networkUser"
+	region := getTestRegionFromEnv()
+	subnetwork := fmt.Sprintf("tf-test-%s", acctest.RandString(10))
+
+	resource.Test(t, resource.TestCase{
+		PreCheck:  func() { testAccPreCheck(t) },
+		Providers: testAccProviders,
+		Steps: []resource.TestStep{
+			{
+				Config: testAccComputeSubnetworkIamPolicy_basic(account, region, subnetwork, role),
+			},
+			// Test a few import formats
+			{
+				ResourceName:      "google_compute_subnetwork_iam_policy.foo",
+				ImportStateId:     fmt.Sprintf("projects/%s/regions/%s/subnetworks/%s", project, region, subnetwork),
+				ImportState:       true,
+				ImportStateVerify: true,
+			},
+			{
+				ResourceName:      "google_compute_subnetwork_iam_policy.foo",
+				ImportStateId:     fmt.Sprintf("%s/%s/%s", project, region, subnetwork),
+				ImportState:       true,
+				ImportStateVerify: true,
+			},
+			{
+				ResourceName:      "google_compute_subnetwork_iam_policy.foo",
+				ImportStateId:     fmt.Sprintf("%s/%s", region, subnetwork),
+				ImportState:       true,
+				ImportStateVerify: true,
+			},
+			{
+				ResourceName:      "google_compute_subnetwork_iam_policy.foo",
+				ImportStateId:     fmt.Sprintf("%s", subnetwork),
+				ImportState:       true,
+				ImportStateVerify: true,
+			},
+		},
+	})
+}
+
+func testAccComputeSubnetworkIamBinding_basic(account, region, subnetworkName, roleId string) string {
+	return fmt.Sprintf(`
+resource "google_service_account" "test_account" {
+  account_id   = "%s"
+  display_name = "Iam Testing Account"
+}
+
+resource "google_compute_network" "network" {
+  name = "%s"
+  auto_create_subnetworks = false
+}
+
+resource "google_compute_subnetwork" "subnetwork" {
+  name = "%s"
+  region = "%s"
+  ip_cidr_range = "10.1.0.0/16"
+  network = "${google_compute_network.network.name}"
+}
+
+resource "google_compute_subnetwork_iam_binding" "foo" {
+  project     = "${google_compute_subnetwork.subnetwork.project}"
+  region      = "${google_compute_subnetwork.subnetwork.region}"
+  subnetwork  = "${google_compute_subnetwork.subnetwork.name}"
+  role        = "%s"
+  members     = ["serviceAccount:${google_service_account.test_account.email}"]
+}
+`, account, subnetworkName, subnetworkName, region, roleId)
+}
+
+func testAccComputeSubnetworkIamBinding_update(account, region, subnetworkName, roleId string) string {
+	return fmt.Sprintf(`
+resource "google_service_account" "test_account" {
+  account_id   = "%s"
+  display_name = "Iam Testing Account"
+}
+
+resource "google_service_account" "test_account_2" {
+  account_id   = "%s-2"
+  display_name = "Iam Testing Account"
+}
+
+resource "google_compute_network" "network" {
+  name = "%s"
+  auto_create_subnetworks = false
+}
+
+resource "google_compute_subnetwork" "subnetwork" {
+  name = "%s"
+  region = "%s"
+  ip_cidr_range = "10.1.0.0/16"
+  network = "${google_compute_network.network.name}"
+}
+
+resource "google_compute_subnetwork_iam_binding" "foo" {
+  project     = "${google_compute_subnetwork.subnetwork.project}"
+  region      = "${google_compute_subnetwork.subnetwork.region}"
+  subnetwork  = "${google_compute_subnetwork.subnetwork.name}"
+  role         = "%s"
+  members      = [
+    "serviceAccount:${google_service_account.test_account.email}",
+    "serviceAccount:${google_service_account.test_account_2.email}"
+  ]
+}
+`, account, account, subnetworkName, subnetworkName, region, roleId)
+}
+
+func testAccComputeSubnetworkIamMember_basic(account, region, subnetworkName, roleId string) string {
+	return fmt.Sprintf(`
+resource "google_service_account" "test_account" {
+  account_id   = "%s"
+  display_name = "Iam Testing Account"
+}
+
+resource "google_compute_network" "network" {
+  name = "%s"
+  auto_create_subnetworks = false
+}
+
+resource "google_compute_subnetwork" "subnetwork" {
+  name = "%s"
+  region = "%s"
+  ip_cidr_range = "10.1.0.0/16"
+  network = "${google_compute_network.network.name}"
+}
+
+resource "google_compute_subnetwork_iam_member" "foo" {
+  project     = "${google_compute_subnetwork.subnetwork.project}"
+  region      = "${google_compute_subnetwork.subnetwork.region}"
+  subnetwork  = "${google_compute_subnetwork.subnetwork.name}"
+  role        = "%s"
+  member      = "serviceAccount:${google_service_account.test_account.email}"
+}
+`, account, subnetworkName, subnetworkName, region, roleId)
+}
+
+func testAccComputeSubnetworkIamPolicy_basic(account, region, subnetworkName, roleId string) string {
+	return fmt.Sprintf(`
+resource "google_service_account" "test_account" {
+  account_id   = "%s"
+  display_name = "Iam Testing Account"
+}
+
+resource "google_compute_network" "network" {
+  name = "%s"
+  auto_create_subnetworks = false
+}
+
+resource "google_compute_subnetwork" "subnetwork" {
+  name = "%s"
+  region = "%s"
+  ip_cidr_range = "10.1.0.0/16"
+  network = "${google_compute_network.network.name}"
+}
+
+data "google_iam_policy" "foo" {
+	binding {
+		role = "%s"
+
+		members = ["serviceAccount:${google_service_account.test_account.email}"]
+	}
+}
+
+resource "google_compute_subnetwork_iam_policy" "foo" {
+  project     = "${google_compute_subnetwork.subnetwork.project}"
+  region      = "${google_compute_subnetwork.subnetwork.region}"
+  subnetwork  = "${google_compute_subnetwork.subnetwork.name}"
+  policy_data = "${data.google_iam_policy.foo.policy_data}"
+}
+`, account, subnetworkName, subnetworkName, region, roleId)
+}