diff --git a/google/iam_pubsub_subscription.go b/google/iam_pubsub_subscription.go index 04dc145928d..ff275d6ccf3 100644 --- a/google/iam_pubsub_subscription.go +++ b/google/iam_pubsub_subscription.go @@ -2,6 +2,7 @@ package google import ( "fmt" + "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform/helper/schema" "google.golang.org/api/cloudresourcemanager/v1" @@ -90,3 +91,22 @@ func (u *PubsubSubscriptionIamUpdater) GetMutexKey() string { func (u *PubsubSubscriptionIamUpdater) DescribeResource() string { return fmt.Sprintf("pubsub subscription %q", u.subscription) } + +// v1 and v2beta policy are identical +func resourceManagerToPubsubPolicy(in *cloudresourcemanager.Policy) (*pubsub.Policy, error) { + out := &pubsub.Policy{} + err := Convert(in, out) + if err != nil { + return nil, errwrap.Wrapf("Cannot convert a v1 policy to a pubsub policy: {{err}}", err) + } + return out, nil +} + +func pubsubToResourceManagerPolicy(in *pubsub.Policy) (*cloudresourcemanager.Policy, error) { + out := &cloudresourcemanager.Policy{} + err := Convert(in, out) + if err != nil { + return nil, errwrap.Wrapf("Cannot convert a pubsub policy to a v1 policy: {{err}}", err) + } + return out, nil +} diff --git a/google/iam_pubsub_topic.go b/google/iam_pubsub_topic.go index 5e432d70978..fa98d84d7f4 100644 --- a/google/iam_pubsub_topic.go +++ b/google/iam_pubsub_topic.go @@ -6,73 +6,122 @@ import ( "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform/helper/schema" "google.golang.org/api/cloudresourcemanager/v1" - "google.golang.org/api/pubsub/v1" ) -var IamPubsubTopicSchema = map[string]*schema.Schema{ +var PubsubTopicIamSchema = map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + }, "topic": { Type: schema.TypeString, Required: true, ForceNew: true, DiffSuppressFunc: compareSelfLinkOrResourceName, }, - "project": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, } type PubsubTopicIamUpdater struct { - topic string - Config *Config + project string + topic string + d *schema.ResourceData + Config *Config } -func NewPubsubTopicIamUpdater(d *schema.ResourceData, config *Config) (ResourceIamUpdater, error) { +func PubsubTopicIamUpdaterProducer(d *schema.ResourceData, config *Config) (ResourceIamUpdater, error) { + values := make(map[string]string) + project, err := getProject(d, config) if err != nil { return nil, err } - topic := getComputedTopicName(project, d.Get("topic").(string)) + // While this may be overridden by the "project" value from getImportIdQualifiers below, + // setting project here ensures the value is set even if the value set in config is the short + // name or otherwise doesn't include the project. + values["project"] = project - return &PubsubTopicIamUpdater{ - topic: topic, - Config: config, - }, nil + m, err := getImportIdQualifiers([]string{"projects/(?P[^/]+)/topics/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)"}, d, config, d.Get("topic").(string)) + if err != nil { + return nil, err + } + + for k, v := range m { + values[k] = v + } + + u := &PubsubTopicIamUpdater{ + project: values["project"], + topic: values["topic"], + d: d, + Config: config, + } + d.SetId(u.GetResourceId()) + + return u, nil } -func PubsubTopicIdParseFunc(d *schema.ResourceData, _ *Config) error { - d.Set("topic", d.Id()) +func PubsubTopicIdParseFunc(d *schema.ResourceData, config *Config) error { + values := make(map[string]string) + + project, err := getProject(d, config) + if err != nil { + return err + } + + values["project"] = project + + m, err := getImportIdQualifiers([]string{"projects/(?P[^/]+)/topics/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)"}, d, config, d.Id()) + if err != nil { + return err + } + + for k, v := range m { + values[k] = v + } + + u := &PubsubTopicIamUpdater{ + project: values["project"], + topic: values["topic"], + d: d, + Config: config, + } + d.Set("topic", u.GetResourceId()) + d.SetId(u.GetResourceId()) return nil } func (u *PubsubTopicIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { - p, err := u.Config.clientPubsub.Projects.Topics.GetIamPolicy(u.topic).Do() + url := u.qualifyTopicUrl("getIamPolicy") + policy, err := sendRequest(u.Config, "GET", url, nil) if err != nil { return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err) } - v1Policy, err := pubsubToResourceManagerPolicy(p) + out := &cloudresourcemanager.Policy{} + err = Convert(policy, out) if err != nil { - return nil, err + return nil, errwrap.Wrapf("Cannot convert a policy to a resource manager policy: {{err}}", err) } - return v1Policy, nil + return out, nil } func (u *PubsubTopicIamUpdater) SetResourceIamPolicy(policy *cloudresourcemanager.Policy) error { - pubsubPolicy, err := resourceManagerToPubsubPolicy(policy) + json, err := ConvertToMap(policy) if err != nil { return err } - _, err = u.Config.clientPubsub.Projects.Topics.SetIamPolicy(u.topic, &pubsub.SetIamPolicyRequest{ - Policy: pubsubPolicy, - }).Do() + obj := make(map[string]interface{}) + obj["policy"] = json + + url := u.qualifyTopicUrl("setIamPolicy") + _, err = sendRequestWithTimeout(u.Config, "POST", url, obj, u.d.Timeout(schema.TimeoutCreate)) if err != nil { return errwrap.Wrapf(fmt.Sprintf("Error setting IAM policy for %s: {{err}}", u.DescribeResource()), err) } @@ -80,33 +129,18 @@ func (u *PubsubTopicIamUpdater) SetResourceIamPolicy(policy *cloudresourcemanage return nil } +func (u *PubsubTopicIamUpdater) qualifyTopicUrl(methodIdentifier string) string { + return fmt.Sprintf("https://pubsub.googleapis.com/v1/%s:%s", fmt.Sprintf("projects/%s/topics/%s", u.project, u.topic), methodIdentifier) +} + func (u *PubsubTopicIamUpdater) GetResourceId() string { - return u.topic + return fmt.Sprintf("projects/%s/topics/%s", u.project, u.topic) } func (u *PubsubTopicIamUpdater) GetMutexKey() string { - return fmt.Sprintf("iam-pubsub-topic-%s", u.topic) + return fmt.Sprintf("iam-pubsub-topic-%s", u.GetResourceId()) } func (u *PubsubTopicIamUpdater) DescribeResource() string { - return fmt.Sprintf("pubsub topic %q", u.topic) -} - -// v1 and v2beta policy are identical -func resourceManagerToPubsubPolicy(in *cloudresourcemanager.Policy) (*pubsub.Policy, error) { - out := &pubsub.Policy{} - err := Convert(in, out) - if err != nil { - return nil, errwrap.Wrapf("Cannot convert a v1 policy to a pubsub policy: {{err}}", err) - } - return out, nil -} - -func pubsubToResourceManagerPolicy(in *pubsub.Policy) (*cloudresourcemanager.Policy, error) { - out := &cloudresourcemanager.Policy{} - err := Convert(in, out) - if err != nil { - return nil, errwrap.Wrapf("Cannot convert a pubsub policy to a v1 policy: {{err}}", err) - } - return out, nil + return fmt.Sprintf("pubsub topic %q", u.GetResourceId()) } diff --git a/google/iam_pubsub_topic_generated_test.go b/google/iam_pubsub_topic_generated_test.go new file mode 100644 index 00000000000..33e718df51f --- /dev/null +++ b/google/iam_pubsub_topic_generated_test.go @@ -0,0 +1,190 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccPubsubTopicIamBindingGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(10), + "role": "roles/editor", + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPubsubTopicDestroy, + Steps: []resource.TestStep{ + { + Config: testAccPubsubTopicIamBinding_basicGenerated(context), + }, + { + ResourceName: "google_pubsub_topic_iam_binding.foo", + ImportStateId: fmt.Sprintf("projects/%s/topics/%s roles/editor", getTestProjectFromEnv(), fmt.Sprintf("example-topic-%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Iam Binding update + Config: testAccPubsubTopicIamBinding_updateGenerated(context), + }, + { + ResourceName: "google_pubsub_topic_iam_binding.foo", + ImportStateId: fmt.Sprintf("projects/%s/topics/%s roles/editor", getTestProjectFromEnv(), fmt.Sprintf("example-topic-%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccPubsubTopicIamMemberGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(10), + "role": "roles/editor", + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPubsubTopicDestroy, + Steps: []resource.TestStep{ + { + // Test Iam Member creation (no update for member, no need to test) + Config: testAccPubsubTopicIamMember_basicGenerated(context), + }, + { + ResourceName: "google_pubsub_topic_iam_member.foo", + ImportStateId: fmt.Sprintf("projects/%s/topics/%s roles/editor user:admin@hashicorptest.com", getTestProjectFromEnv(), fmt.Sprintf("example-topic-%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccPubsubTopicIamPolicyGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(10), + "role": "roles/editor", + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPubsubTopicDestroy, + Steps: []resource.TestStep{ + { + Config: testAccPubsubTopicIamPolicy_basicGenerated(context), + }, + { + ResourceName: "google_pubsub_topic_iam_policy.foo", + ImportStateId: fmt.Sprintf("projects/%s/topics/%s", getTestProjectFromEnv(), fmt.Sprintf("example-topic-%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccPubsubTopicIamMember_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_pubsub_topic" "example" { + name = "example-topic-%{random_suffix}" + + labels = { + foo = "bar" + } +} + +resource "google_pubsub_topic_iam_member" "foo" { + topic = "${google_pubsub_topic.example.id}" + role = "%{role}" + member = "user:admin@hashicorptest.com" +} +`, context) +} + +func testAccPubsubTopicIamPolicy_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_pubsub_topic" "example" { + name = "example-topic-%{random_suffix}" + + labels = { + foo = "bar" + } +} + +data "google_iam_policy" "foo" { + binding { + role = "%{role}" + members = ["user:admin@hashicorptest.com"] + } +} + +resource "google_pubsub_topic_iam_policy" "foo" { + topic = "${google_pubsub_topic.example.id}" + policy_data = "${data.google_iam_policy.foo.policy_data}" +} +`, context) +} + +func testAccPubsubTopicIamBinding_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_pubsub_topic" "example" { + name = "example-topic-%{random_suffix}" + + labels = { + foo = "bar" + } +} + +resource "google_pubsub_topic_iam_binding" "foo" { + topic = "${google_pubsub_topic.example.id}" + role = "%{role}" + members = ["user:admin@hashicorptest.com"] +} +`, context) +} + +func testAccPubsubTopicIamBinding_updateGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_pubsub_topic" "example" { + name = "example-topic-%{random_suffix}" + + labels = { + foo = "bar" + } +} + +resource "google_pubsub_topic_iam_binding" "foo" { + topic = "${google_pubsub_topic.example.id}" + role = "%{role}" + members = ["user:admin@hashicorptest.com", "user:paddy@hashicorp.com"] +} +`, context) +} diff --git a/google/iam_source_repo_repository.go b/google/iam_source_repo_repository.go new file mode 100644 index 00000000000..c32434abf8c --- /dev/null +++ b/google/iam_source_repo_repository.go @@ -0,0 +1,146 @@ +package google + +import ( + "fmt" + + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" + "google.golang.org/api/cloudresourcemanager/v1" +) + +var SourceRepoRepositoryIamSchema = map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + }, + "repository": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + }, +} + +type SourceRepoRepositoryIamUpdater struct { + project string + repository string + d *schema.ResourceData + Config *Config +} + +func SourceRepoRepositoryIamUpdaterProducer(d *schema.ResourceData, config *Config) (ResourceIamUpdater, error) { + values := make(map[string]string) + + project, err := getProject(d, config) + if err != nil { + return nil, err + } + + // While this may be overridden by the "project" value from getImportIdQualifiers below, + // setting project here ensures the value is set even if the value set in config is the short + // name or otherwise doesn't include the project. + values["project"] = project + + m, err := getImportIdQualifiers([]string{"projects/(?P[^/]+)/repos/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)"}, d, config, d.Get("repository").(string)) + if err != nil { + return nil, err + } + + for k, v := range m { + values[k] = v + } + + u := &SourceRepoRepositoryIamUpdater{ + project: values["project"], + repository: values["repository"], + d: d, + Config: config, + } + d.SetId(u.GetResourceId()) + + return u, nil +} + +func SourceRepoRepositoryIdParseFunc(d *schema.ResourceData, config *Config) error { + values := make(map[string]string) + + project, err := getProject(d, config) + if err != nil { + return err + } + + values["project"] = project + + m, err := getImportIdQualifiers([]string{"projects/(?P[^/]+)/repos/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)"}, d, config, d.Id()) + if err != nil { + return err + } + + for k, v := range m { + values[k] = v + } + + u := &SourceRepoRepositoryIamUpdater{ + project: values["project"], + repository: values["repository"], + d: d, + Config: config, + } + d.Set("repository", u.GetResourceId()) + d.SetId(u.GetResourceId()) + return nil +} + +func (u *SourceRepoRepositoryIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { + url := u.qualifyRepositoryUrl("getIamPolicy") + + policy, err := sendRequest(u.Config, "GET", url, nil) + if err != nil { + return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + out := &cloudresourcemanager.Policy{} + err = Convert(policy, out) + if err != nil { + return nil, errwrap.Wrapf("Cannot convert a policy to a resource manager policy: {{err}}", err) + } + + return out, nil +} + +func (u *SourceRepoRepositoryIamUpdater) SetResourceIamPolicy(policy *cloudresourcemanager.Policy) error { + json, err := ConvertToMap(policy) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + obj["policy"] = json + + url := u.qualifyRepositoryUrl("setIamPolicy") + + _, err = sendRequestWithTimeout(u.Config, "POST", url, obj, u.d.Timeout(schema.TimeoutCreate)) + if err != nil { + return errwrap.Wrapf(fmt.Sprintf("Error setting IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + return nil +} + +func (u *SourceRepoRepositoryIamUpdater) qualifyRepositoryUrl(methodIdentifier string) string { + return fmt.Sprintf("https://sourcerepo.googleapis.com/v1/%s:%s", fmt.Sprintf("projects/%s/repos/%s", u.project, u.repository), methodIdentifier) +} + +func (u *SourceRepoRepositoryIamUpdater) GetResourceId() string { + return fmt.Sprintf("%s/%s", u.project, u.repository) +} + +func (u *SourceRepoRepositoryIamUpdater) GetMutexKey() string { + return fmt.Sprintf("iam-sourcerepo-repository-%s", u.GetResourceId()) +} + +func (u *SourceRepoRepositoryIamUpdater) DescribeResource() string { + return fmt.Sprintf("sourcerepo repository %q", u.GetResourceId()) +} diff --git a/google/iam_source_repo_repository_generated_test.go b/google/iam_source_repo_repository_generated_test.go new file mode 100644 index 00000000000..d94d78af64a --- /dev/null +++ b/google/iam_source_repo_repository_generated_test.go @@ -0,0 +1,174 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccSourceRepoRepositoryIamBindingGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(10), + "role": "roles/editor", + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckSourceRepoRepositoryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccSourceRepoRepositoryIamBinding_basicGenerated(context), + }, + { + ResourceName: "google_sourcerepo_repository_iam_binding.foo", + ImportStateId: fmt.Sprintf("projects/%s/repos/%s roles/editor", getTestProjectFromEnv(), fmt.Sprintf("my-repository-%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Iam Binding update + Config: testAccSourceRepoRepositoryIamBinding_updateGenerated(context), + }, + { + ResourceName: "google_sourcerepo_repository_iam_binding.foo", + ImportStateId: fmt.Sprintf("projects/%s/repos/%s roles/editor", getTestProjectFromEnv(), fmt.Sprintf("my-repository-%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccSourceRepoRepositoryIamMemberGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(10), + "role": "roles/editor", + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckSourceRepoRepositoryDestroy, + Steps: []resource.TestStep{ + { + // Test Iam Member creation (no update for member, no need to test) + Config: testAccSourceRepoRepositoryIamMember_basicGenerated(context), + }, + { + ResourceName: "google_sourcerepo_repository_iam_member.foo", + ImportStateId: fmt.Sprintf("projects/%s/repos/%s roles/editor user:admin@hashicorptest.com", getTestProjectFromEnv(), fmt.Sprintf("my-repository-%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccSourceRepoRepositoryIamPolicyGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(10), + "role": "roles/editor", + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckSourceRepoRepositoryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccSourceRepoRepositoryIamPolicy_basicGenerated(context), + }, + { + ResourceName: "google_sourcerepo_repository_iam_policy.foo", + ImportStateId: fmt.Sprintf("projects/%s/repos/%s", getTestProjectFromEnv(), fmt.Sprintf("my-repository-%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccSourceRepoRepositoryIamMember_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_sourcerepo_repository" "my-repo" { + name = "my-repository-%{random_suffix}" +} + +resource "google_sourcerepo_repository_iam_member" "foo" { + repository = "${google_sourcerepo_repository.my-repo.id}" + role = "%{role}" + member = "user:admin@hashicorptest.com" +} +`, context) +} + +func testAccSourceRepoRepositoryIamPolicy_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_sourcerepo_repository" "my-repo" { + name = "my-repository-%{random_suffix}" +} + +data "google_iam_policy" "foo" { + binding { + role = "%{role}" + members = ["user:admin@hashicorptest.com"] + } +} + +resource "google_sourcerepo_repository_iam_policy" "foo" { + repository = "${google_sourcerepo_repository.my-repo.id}" + policy_data = "${data.google_iam_policy.foo.policy_data}" +} +`, context) +} + +func testAccSourceRepoRepositoryIamBinding_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_sourcerepo_repository" "my-repo" { + name = "my-repository-%{random_suffix}" +} + +resource "google_sourcerepo_repository_iam_binding" "foo" { + repository = "${google_sourcerepo_repository.my-repo.id}" + role = "%{role}" + members = ["user:admin@hashicorptest.com"] +} +`, context) +} + +func testAccSourceRepoRepositoryIamBinding_updateGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_sourcerepo_repository" "my-repo" { + name = "my-repository-%{random_suffix}" +} + +resource "google_sourcerepo_repository_iam_binding" "foo" { + repository = "${google_sourcerepo_repository.my-repo.id}" + role = "%{role}" + members = ["user:admin@hashicorptest.com", "user:paddy@hashicorp.com"] +} +`, context) +} diff --git a/google/import.go b/google/import.go index 290a34613d5..f65e42cbc43 100644 --- a/google/import.go +++ b/google/import.go @@ -97,3 +97,76 @@ func setDefaultValues(idRegex string, d TerraformResourceData, config *Config) e } return nil } + +// Parse an import id extracting field values using the given list of regexes. +// They are applied in order. The first in the list is tried first. +// This does not mutate any of the parameters, returning a map of matches +// +// e.g: +// - projects/(?P[^/]+)/regions/(?P[^/]+)/subnetworks/(?P[^/]+) (applied first) +// - (?P[^/]+)/(?P[^/]+)/(?P[^/]+), +// - (?P[^/]+) (applied last) +func getImportIdQualifiers(idRegexes []string, d TerraformResourceData, config *Config, id string) (map[string]string, error) { + for _, idFormat := range idRegexes { + re, err := regexp.Compile(idFormat) + + if err != nil { + log.Printf("[DEBUG] Could not compile %s.", idFormat) + return nil, fmt.Errorf("Import is not supported. Invalid regex formats.") + } + + if fieldValues := re.FindStringSubmatch(id); fieldValues != nil { + var result map[string]string + result = make(map[string]string) + log.Printf("[DEBUG] matching ID %s to regex %s.", id, idFormat) + // Starting at index 1, the first match is the full string. + for i := 1; i < len(fieldValues); i++ { + fieldName := re.SubexpNames()[i] + fieldValue := fieldValues[i] + result[fieldName] = fieldValue + } + + // The first id format is applied first and contains all the fields. + defaults, err := getDefaultValues(idRegexes[0], d, config) + if err != nil { + return nil, err + } + + for k, v := range defaults { + result[k] = v + } + + return result, nil + } + } + return nil, fmt.Errorf("Resource id %q doesn't match any of the accepted formats: %v", id, idRegexes) +} + +// Returns a set of default values that are contained in a regular expression +// This does not mutate any parameters, instead returning a map of defaults +func getDefaultValues(idRegex string, d TerraformResourceData, config *Config) (map[string]string, error) { + var result map[string]string + result = make(map[string]string) + if _, ok := d.GetOk("project"); !ok && strings.Contains(idRegex, "?P") { + project, err := getProject(d, config) + if err != nil { + return nil, err + } + result["project"] = project + } + if _, ok := d.GetOk("region"); !ok && strings.Contains(idRegex, "?P") { + region, err := getRegion(d, config) + if err != nil { + return nil, err + } + result["region"] = region + } + if _, ok := d.GetOk("zone"); !ok && strings.Contains(idRegex, "?P") { + zone, err := getZone(d, config) + if err != nil { + return nil, err + } + result["zone"] = zone + } + return result, nil +} diff --git a/google/provider.go b/google/provider.go index c76ef8d3f58..72b9ca4ee4b 100644 --- a/google/provider.go +++ b/google/provider.go @@ -301,9 +301,6 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_project_organization_policy": resourceGoogleProjectOrganizationPolicy(), "google_project_usage_export_bucket": resourceProjectUsageBucket(), "google_project_services": resourceGoogleProjectServices(), - "google_pubsub_topic_iam_binding": ResourceIamBindingWithImport(IamPubsubTopicSchema, NewPubsubTopicIamUpdater, PubsubTopicIdParseFunc), - "google_pubsub_topic_iam_member": ResourceIamMemberWithImport(IamPubsubTopicSchema, NewPubsubTopicIamUpdater, PubsubTopicIdParseFunc), - "google_pubsub_topic_iam_policy": ResourceIamPolicyWithImport(IamPubsubTopicSchema, NewPubsubTopicIamUpdater, PubsubTopicIdParseFunc), "google_pubsub_subscription_iam_binding": ResourceIamBindingWithImport(IamPubsubSubscriptionSchema, NewPubsubSubscriptionIamUpdater, PubsubSubscriptionIdParseFunc), "google_pubsub_subscription_iam_member": ResourceIamMemberWithImport(IamPubsubSubscriptionSchema, NewPubsubSubscriptionIamUpdater, PubsubSubscriptionIdParseFunc), "google_pubsub_subscription_iam_policy": ResourceIamPolicyWithImport(IamPubsubSubscriptionSchema, NewPubsubSubscriptionIamUpdater, PubsubSubscriptionIdParseFunc), diff --git a/google/provider_pubsub_gen.go b/google/provider_pubsub_gen.go index 9c1a14ddef2..0a62cac39e7 100644 --- a/google/provider_pubsub_gen.go +++ b/google/provider_pubsub_gen.go @@ -30,6 +30,9 @@ var PubsubCustomEndpointEntry = &schema.Schema{ } var GeneratedPubsubResourcesMap = map[string]*schema.Resource{ - "google_pubsub_topic": resourcePubsubTopic(), - "google_pubsub_subscription": resourcePubsubSubscription(), + "google_pubsub_topic": resourcePubsubTopic(), + "google_pubsub_topic_iam_binding": ResourceIamBindingWithImport(PubsubTopicIamSchema, PubsubTopicIamUpdaterProducer, PubsubTopicIdParseFunc), + "google_pubsub_topic_iam_member": ResourceIamMemberWithImport(PubsubTopicIamSchema, PubsubTopicIamUpdaterProducer, PubsubTopicIdParseFunc), + "google_pubsub_topic_iam_policy": ResourceIamPolicyWithImport(PubsubTopicIamSchema, PubsubTopicIamUpdaterProducer, PubsubTopicIdParseFunc), + "google_pubsub_subscription": resourcePubsubSubscription(), } diff --git a/google/provider_source_repo_gen.go b/google/provider_source_repo_gen.go index c0452752d73..2a2e3acf325 100644 --- a/google/provider_source_repo_gen.go +++ b/google/provider_source_repo_gen.go @@ -30,5 +30,8 @@ var SourceRepoCustomEndpointEntry = &schema.Schema{ } var GeneratedSourceRepoResourcesMap = map[string]*schema.Resource{ - "google_sourcerepo_repository": resourceSourceRepoRepository(), + "google_sourcerepo_repository": resourceSourceRepoRepository(), + "google_sourcerepo_repository_iam_binding": ResourceIamBindingWithImport(SourceRepoRepositoryIamSchema, SourceRepoRepositoryIamUpdaterProducer, SourceRepoRepositoryIdParseFunc), + "google_sourcerepo_repository_iam_member": ResourceIamMemberWithImport(SourceRepoRepositoryIamSchema, SourceRepoRepositoryIamUpdaterProducer, SourceRepoRepositoryIdParseFunc), + "google_sourcerepo_repository_iam_policy": ResourceIamPolicyWithImport(SourceRepoRepositoryIamSchema, SourceRepoRepositoryIamUpdaterProducer, SourceRepoRepositoryIdParseFunc), } diff --git a/google/resource_pubsub_topic_iam_test.go b/google/resource_pubsub_topic_iam_test.go deleted file mode 100644 index 8a1d61652bd..00000000000 --- a/google/resource_pubsub_topic_iam_test.go +++ /dev/null @@ -1,276 +0,0 @@ -package google - -import ( - "fmt" - "reflect" - "sort" - "testing" - - "github.com/hashicorp/terraform/helper/acctest" - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" -) - -func TestAccPubsubTopicIamBinding(t *testing.T) { - t.Parallel() - - topic := "test-topic-iam-" + acctest.RandString(10) - account := "test-topic-iam-" + acctest.RandString(10) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - // Test IAM Binding creation - Config: testAccPubsubTopicIamBinding_basic(topic, account), - Check: testAccCheckPubsubTopicIam(topic, "roles/pubsub.publisher", []string{ - fmt.Sprintf("serviceAccount:%s-1@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), - }), - }, - { - ResourceName: "google_pubsub_topic_iam_binding.foo", - ImportStateId: fmt.Sprintf("%s roles/pubsub.publisher", getComputedTopicName(getTestProjectFromEnv(), topic)), - ImportState: true, - ImportStateVerify: true, - }, - { - // Test IAM Binding update - Config: testAccPubsubTopicIamBinding_update(topic, account), - Check: testAccCheckPubsubTopicIam(topic, "roles/pubsub.publisher", []string{ - fmt.Sprintf("serviceAccount:%s-1@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), - fmt.Sprintf("serviceAccount:%s-2@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), - }), - }, - { - ResourceName: "google_pubsub_topic_iam_binding.foo", - ImportStateId: fmt.Sprintf("%s roles/pubsub.publisher", getComputedTopicName(getTestProjectFromEnv(), topic)), - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccPubsubTopicIamBinding_topicName(t *testing.T) { - t.Parallel() - - topic := "test-topic-iam-" + acctest.RandString(10) - account := "test-topic-iam-" + acctest.RandString(10) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - // Test IAM Binding creation - Config: testAccPubsubTopicIamBinding_topicName(topic, account), - Check: testAccCheckPubsubTopicIam(topic, "roles/pubsub.publisher", []string{ - fmt.Sprintf("serviceAccount:%s-1@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), - }), - }, - // No import step- imports want the resource to be defined using the full id as the topic - }, - }) -} - -func TestAccPubsubTopicIamMember(t *testing.T) { - t.Parallel() - - topic := "test-topic-iam-" + acctest.RandString(10) - account := "test-topic-iam-" + acctest.RandString(10) - accountEmail := fmt.Sprintf("%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()) - - 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: testAccPubsubTopicIamMember_basic(topic, account), - Check: testAccCheckPubsubTopicIam(topic, "roles/pubsub.publisher", []string{ - fmt.Sprintf("serviceAccount:%s", accountEmail), - }), - }, - { - ResourceName: "google_pubsub_topic_iam_member.foo", - ImportStateId: fmt.Sprintf("%s roles/pubsub.publisher serviceAccount:%s", getComputedTopicName(getTestProjectFromEnv(), topic), accountEmail), - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccPubsubTopicIamPolicy(t *testing.T) { - t.Parallel() - - topic := "test-topic-iam-" + acctest.RandString(10) - account := "test-topic-iam-" + acctest.RandString(10) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: testAccPubsubTopicIamPolicy_basic(topic, account, "roles/pubsub.publisher"), - Check: testAccCheckPubsubTopicIam(topic, "roles/pubsub.publisher", []string{ - fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), - }), - }, - { - Config: testAccPubsubTopicIamPolicy_basic(topic, account, "roles/pubsub.subscriber"), - Check: testAccCheckPubsubTopicIam(topic, "roles/pubsub.subscriber", []string{ - fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), - }), - }, - { - ResourceName: "google_pubsub_topic_iam_policy.foo", - ImportStateId: getComputedTopicName(getTestProjectFromEnv(), topic), - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func testAccCheckPubsubTopicIam(topic, role string, members []string) resource.TestCheckFunc { - return func(s *terraform.State) error { - config := testAccProvider.Meta().(*Config) - p, err := config.clientPubsub.Projects.Topics.GetIamPolicy(getComputedTopicName(getTestProjectFromEnv(), topic)).Do() - if err != nil { - return err - } - - for _, binding := range p.Bindings { - if binding.Role == role { - sort.Strings(members) - sort.Strings(binding.Members) - - if reflect.DeepEqual(members, binding.Members) { - return nil - } - - return fmt.Errorf("Binding found but expected members is %v, got %v", members, binding.Members) - } - } - - return fmt.Errorf("No binding for role %q", role) - } -} - -func testAccPubsubTopicIamBinding_topicName(topic, account string) string { - return fmt.Sprintf(` -resource "google_pubsub_topic" "topic" { - name = "%s" -} - -resource "google_service_account" "test-account-1" { - account_id = "%s-1" - display_name = "Iam Testing Account" -} - -resource "google_pubsub_topic_iam_binding" "foo" { - project = "%s" - topic = "${google_pubsub_topic.topic.name}" - role = "roles/pubsub.publisher" - members = [ - "serviceAccount:${google_service_account.test-account-1.email}", - ] -} -`, topic, account, getTestProjectFromEnv()) -} - -func testAccPubsubTopicIamBinding_basic(topic, account string) string { - return fmt.Sprintf(` -resource "google_pubsub_topic" "topic" { - name = "%s" -} - -resource "google_service_account" "test-account-1" { - account_id = "%s-1" - display_name = "Iam Testing Account" -} - -resource "google_pubsub_topic_iam_binding" "foo" { - # use the id instead of the name because it's more compatible with import - topic = "${google_pubsub_topic.topic.id}" - role = "roles/pubsub.publisher" - members = [ - "serviceAccount:${google_service_account.test-account-1.email}", - ] -} -`, topic, account) -} - -func testAccPubsubTopicIamBinding_update(topic, account string) string { - return fmt.Sprintf(` -resource "google_pubsub_topic" "topic" { - name = "%s" -} - -resource "google_service_account" "test-account-1" { - account_id = "%s-1" - display_name = "Iam Testing Account" -} - -resource "google_service_account" "test-account-2" { - account_id = "%s-2" - display_name = "Iam Testing Account" -} - -resource "google_pubsub_topic_iam_binding" "foo" { - # use the id instead of the name because it's more compatible with import - topic = "${google_pubsub_topic.topic.id}" - role = "roles/pubsub.publisher" - members = [ - "serviceAccount:${google_service_account.test-account-1.email}", - "serviceAccount:${google_service_account.test-account-2.email}", - ] -} -`, topic, account, account) -} - -func testAccPubsubTopicIamMember_basic(topic, account string) string { - return fmt.Sprintf(` -resource "google_pubsub_topic" "topic" { - name = "%s" -} - -resource "google_service_account" "test-account" { - account_id = "%s" - display_name = "Iam Testing Account" -} - -resource "google_pubsub_topic_iam_member" "foo" { - topic = "${google_pubsub_topic.topic.id}" - role = "roles/pubsub.publisher" - member = "serviceAccount:${google_service_account.test-account.email}" -} -`, topic, account) -} - -func testAccPubsubTopicIamPolicy_basic(topic, account, role string) string { - return fmt.Sprintf(` -resource "google_pubsub_topic" "topic" { - name = "%s" -} - -resource "google_service_account" "test-account" { - account_id = "%s" - display_name = "Iam Testing Account" -} - -data "google_iam_policy" "foo" { - binding { - role = "%s" - members = ["serviceAccount:${google_service_account.test-account.email}"] - } -} - -resource "google_pubsub_topic_iam_policy" "foo" { - topic = "${google_pubsub_topic.topic.id}" - policy_data = "${data.google_iam_policy.foo.policy_data}" -} -`, topic, account, role) -} diff --git a/website/docs/r/pubsub_topic_iam.html.markdown b/website/docs/r/pubsub_topic_iam.html.markdown index 1ea8f2362ec..e792b0e2892 100644 --- a/website/docs/r/pubsub_topic_iam.html.markdown +++ b/website/docs/r/pubsub_topic_iam.html.markdown @@ -1,14 +1,26 @@ --- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- layout: "google" page_title: "Google: google_pubsub_topic_iam" sidebar_current: "docs-google-pubsub-topic-iam" description: |- - Collection of resources to manage IAM policy for a Pubsub Topic. + Collection of resources to manage IAM policy for PubsubTopic --- -# IAM policy for Pubsub Topic - -Three different resources help you manage your IAM policy for pubsub topic. Each of these resources serves a different use case: +# IAM policy for PubsubTopic +Three different resources help you manage your IAM policy for Pubsub Topic. Each of these resources serves a different use case: * `google_pubsub_topic_iam_policy`: Authoritative. Sets the IAM policy for the topic and replaces any existing policy already attached. * `google_pubsub_topic_iam_binding`: Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. Other roles within the IAM policy for the topic are preserved. @@ -18,6 +30,8 @@ Three different resources help you manage your IAM policy for pubsub topic. Each ~> **Note:** `google_pubsub_topic_iam_binding` resources **can be** used in conjunction with `google_pubsub_topic_iam_member` resources **only if** they do not grant privilege to the same role. + + ## google\_pubsub\_topic\_iam\_policy ```hcl @@ -31,7 +45,7 @@ data "google_iam_policy" "admin" { } resource "google_pubsub_topic_iam_policy" "editor" { - topic = "your-topic-name" + topic = "projects/{{project}}/topics/{{topic}}" policy_data = "${data.google_iam_policy.admin.policy_data}" } ``` @@ -40,7 +54,7 @@ resource "google_pubsub_topic_iam_policy" "editor" { ```hcl resource "google_pubsub_topic_iam_binding" "editor" { - topic = "your-topic-name" + topic = "projects/{{project}}/topics/{{topic}}" role = "roles/editor" members = [ "user:jane@example.com", @@ -52,7 +66,7 @@ resource "google_pubsub_topic_iam_binding" "editor" { ```hcl resource "google_pubsub_topic_iam_member" "editor" { - topic = "your-topic-name" + topic = "projects/{{project}}/topics/{{topic}}" role = "roles/editor" member = "user:jane@example.com" } @@ -95,9 +109,11 @@ exported: Pubsub topic IAM resources can be imported using the project, topic name, role and member. ``` -$ terraform import google_pubsub_topic_iam_policy.editor projects/{your-project-id}/topics/{your-topic-name} +$ terraform import google_pubsub_topic_iam_policy.editor projects/{{project}}/topics/{{topic}} +$ terraform import google_pubsub_topic_iam_binding.editor "projects/{{project}}/topics/{{topic}} roles/editor" -$ terraform import google_pubsub_topic_iam_binding.editor "projects/{your-project-id}/topics/{your-topic-name} roles/editor" +$ terraform import google_pubsub_topic_iam_member.editor "projects/{{project}}/topics/{{topic}} roles/editor jane@example.com" +``` -$ terraform import google_pubsub_topic_iam_member.editor "projects/{your-project-id}/topics/{your-topic-name} roles/editor jane@example.com" -``` \ No newline at end of file +-> If you're importing a resource with beta features, make sure to include `-provider=google-beta` +as an argument so that Terraform uses the correct provider to import your resource. diff --git a/website/docs/r/source_repo_repository_iam.html.markdown b/website/docs/r/source_repo_repository_iam.html.markdown new file mode 100644 index 00000000000..c2d848eb9f4 --- /dev/null +++ b/website/docs/r/source_repo_repository_iam.html.markdown @@ -0,0 +1,119 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- +layout: "google" +page_title: "Google: google_sourcerepo_repository_iam" +sidebar_current: "docs-google-sourcerepo-repository-iam" +description: |- + Collection of resources to manage IAM policy for SourceRepoRepository +--- + +# IAM policy for SourceRepoRepository +Three different resources help you manage your IAM policy for SourceRepo Repository. Each of these resources serves a different use case: + +* `google_sourcerepo_repository_iam_policy`: Authoritative. Sets the IAM policy for the repository and replaces any existing policy already attached. +* `google_sourcerepo_repository_iam_binding`: Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. Other roles within the IAM policy for the repository are preserved. +* `google_sourcerepo_repository_iam_member`: Non-authoritative. Updates the IAM policy to grant a role to a new member. Other members for the role for the repository are preserved. + +~> **Note:** `google_sourcerepo_repository_iam_policy` **cannot** be used in conjunction with `google_sourcerepo_repository_iam_binding` and `google_sourcerepo_repository_iam_member` or they will fight over what your policy should be. + +~> **Note:** `google_sourcerepo_repository_iam_binding` resources **can be** used in conjunction with `google_sourcerepo_repository_iam_member` resources **only if** they do not grant privilege to the same role. + + + +## google\_sourcerepo\_repository\_iam\_policy + +```hcl +data "google_iam_policy" "admin" { + binding { + role = "roles/editor" + members = [ + "user:jane@example.com", + ] + } +} + +resource "google_sourcerepo_repository_iam_policy" "editor" { + repository = "{{project}}/{{repository}}" + policy_data = "${data.google_iam_policy.admin.policy_data}" +} +``` + +## google\_sourcerepo\_repository\_iam\_binding + +```hcl +resource "google_sourcerepo_repository_iam_binding" "editor" { + repository = "{{project}}/{{repository}}" + role = "roles/editor" + members = [ + "user:jane@example.com", + ] +} +``` + +## google\_sourcerepo\_repository\_iam\_member + +```hcl +resource "google_sourcerepo_repository_iam_member" "editor" { + repository = "{{project}}/{{repository}}" + role = "roles/editor" + member = "user:jane@example.com" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `repository` - (Required) The repository name or id to bind to attach IAM policy to. + +* `project` - (Optional) The project in which the resource belongs. If it + is not provided, the provider project is used. + +* `member/members` - (Required) Identities that will be granted the privilege in `role`. + Each entry can have one of the following values: + * **allUsers**: A special identifier that represents anyone who is on the internet; with or without a Google account. + * **allAuthenticatedUsers**: A special identifier that represents anyone who is authenticated with a Google account or a service account. + * **user:{emailid}**: An email address that represents a specific Google account. For example, alice@gmail.com or joe@example.com. + * **serviceAccount:{emailid}**: An email address that represents a service account. For example, my-other-app@appspot.gserviceaccount.com. + * **group:{emailid}**: An email address that represents a Google group. For example, admins@example.com. + * **domain:{domain}**: A G Suite domain (primary, instead of alias) name that represents all the users of that domain. For example, google.com or example.com. + +* `role` - (Required) The role that should be applied. Only one + `google_sourcerepo_repository_iam_binding` can be used per role. Note that custom roles must be of the format + `[projects|organizations]/{parent-name}/roles/{role-name}`. + +* `policy_data` - (Required only by `google_sourcerepo_repository_iam_policy`) The policy data generated by + a `google_iam_policy` data source. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are +exported: + +* `etag` - (Computed) The etag of the repository's IAM policy. + +## Import + +SourceRepo repository IAM resources can be imported using the project, repository name, role and member. + +``` +$ terraform import google_sourcerepo_repository_iam_policy.editor {{project}}/{{repository}} +$ terraform import google_sourcerepo_repository_iam_binding.editor "{{project}}/{{repository}} roles/editor" + +$ terraform import google_sourcerepo_repository_iam_member.editor "{{project}}/{{repository}} roles/editor jane@example.com" +``` + +-> If you're importing a resource with beta features, make sure to include `-provider=google-beta` +as an argument so that Terraform uses the correct provider to import your resource.