diff --git a/mmv1/products/alloydb/User.yaml b/mmv1/products/alloydb/User.yaml new file mode 100644 index 000000000000..307f3379a5d1 --- /dev/null +++ b/mmv1/products/alloydb/User.yaml @@ -0,0 +1,95 @@ +# Copyright 2023 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- !ruby/object:Api::Resource +name: 'User' +self_link: '{{cluster}}/users/{{user_id}}' +base_url: '{{cluster}}/users' +create_url: '{{cluster}}/users?userId={{user_id}}' +update_url: '{{cluster}}/users?userId={{user_id}}' +update_verb: :POST +description: 'A database user in an AlloyDB cluster.' +references: !ruby/object:Api::Resource::ReferenceLinks + guides: + 'AlloyDB': 'https://cloud.google.com/alloydb/docs/' + api: 'https://cloud.google.com/alloydb/docs/reference/rest/v1/projects.locations.clusters.users/create' +import_format: ['projects/{{project}}/locations/{{location}}/clusters/{{cluster}}/users/{{user_id}}'] +skip_sweeper: true +autogen_async: true +custom_code: !ruby/object:Provider::Terraform::CustomCode + custom_import: templates/terraform/custom_import/alloydb_user.go.erb +examples: + - !ruby/object:Provider::Terraform::Examples + name: 'alloydb_user_builtin' + primary_resource_id: 'user1' + vars: + alloydb_cluster_name: 'alloydb-cluster' + alloydb_cluster_pass: 'cluster_secret' + alloydb_instance_name: 'alloydb-instance' + alloydb_user_name: 'user1' + alloydb_user_pass: 'user_secret' + network_name: 'alloydb-network' + ignore_read_extra: + - 'password' + - !ruby/object:Provider::Terraform::Examples + name: 'alloydb_user_iam' + primary_resource_id: 'user2' + vars: + alloydb_cluster_name: 'alloydb-cluster' + alloydb_instance_name: 'alloydb-instance' + alloydb_cluster_pass: 'cluster_secret' + alloydb_user_name: 'user2@foo.com' + network_name: 'alloydb-network' +parameters: + - !ruby/object:Api::Type::ResourceRef + name: 'cluster' + description: | + Identifies the alloydb cluster. Must be in the format + 'projects/{project}/locations/{location}/clusters/{cluster_id}' + required: true + immutable: true + resource: 'Cluster' + imports: 'name' + url_param_only: true + - !ruby/object:Api::Type::String + name: 'userId' + required: true + immutable: true + url_param_only: true + description: | + The database role name of the user. + - !ruby/object:Api::Type::Enum + name: 'userType' + required: true + immutable: true + description: | + The type of this user. + values: + - :ALLOYDB_BUILT_IN + - :ALLOYDB_IAM_USER +properties: + - !ruby/object:Api::Type::String + name: 'name' + output: true + description: | + Name of the resource in the form of projects/{project}/locations/{location}/clusters/{cluster}/users/{user}. + - !ruby/object:Api::Type::String + name: 'password' + ignore_read: true + description: | + Password for this database user. + - !ruby/object:Api::Type::Array + name: 'databaseRoles' + item_type: Api::Type::String + description: | + List of database roles this database user has. diff --git a/mmv1/templates/terraform/custom_import/alloydb_user.go.erb b/mmv1/templates/terraform/custom_import/alloydb_user.go.erb new file mode 100644 index 000000000000..188d9e27a5da --- /dev/null +++ b/mmv1/templates/terraform/custom_import/alloydb_user.go.erb @@ -0,0 +1,17 @@ +config := meta.(*transport_tpg.Config) + +// current import_formats can't import fields with forward slashes in their value +if err := tpgresource.ParseImportId([]string{ + "(?P.+)/users/(?P[^/]+)", +}, d, config); err != nil { + return nil, err +} + +// Replace import id for the resource id +id, err := tpgresource.ReplaceVars(d, config, "{{cluster}}/users/{{user_id}}") +if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) +} +d.SetId(id) + +return []*schema.ResourceData{d}, nil diff --git a/mmv1/templates/terraform/examples/alloydb_user_builtin.tf.erb b/mmv1/templates/terraform/examples/alloydb_user_builtin.tf.erb new file mode 100644 index 000000000000..ff49ab77a102 --- /dev/null +++ b/mmv1/templates/terraform/examples/alloydb_user_builtin.tf.erb @@ -0,0 +1,47 @@ +resource "google_alloydb_instance" "default" { + cluster = google_alloydb_cluster.default.name + instance_id = "<%= ctx[:vars]['alloydb_instance_name'] %>" + instance_type = "PRIMARY" + + depends_on = [google_service_networking_connection.vpc_connection] +} + +resource "google_alloydb_cluster" "default" { + cluster_id = "<%= ctx[:vars]['alloydb_cluster_name'] %>" + location = "us-central1" + network = google_compute_network.default.id + + initial_user { + password = "<%= ctx[:vars]['alloydb_cluster_pass'] %>" + } +} + +data "google_project" "project" {} + +resource "google_compute_network" "default" { + name = "<%= ctx[:vars]['network_name'] %>" +} + +resource "google_compute_global_address" "private_ip_alloc" { + name = "<%= ctx[:vars]['alloydb_cluster_name'] %>" + address_type = "INTERNAL" + purpose = "VPC_PEERING" + prefix_length = 16 + network = google_compute_network.default.id +} + +resource "google_service_networking_connection" "vpc_connection" { + network = google_compute_network.default.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.private_ip_alloc.name] +} + +resource "google_alloydb_user" "<%= ctx[:primary_resource_id] %>" { + cluster = google_alloydb_cluster.default.name + user_id = "<%= ctx[:vars]['alloydb_user_name'] %>" + user_type = "ALLOYDB_BUILT_IN" + + password = "<%= ctx[:vars]['alloydb_user_pass'] %>" + database_roles = ["alloydbsuperuser"] + depends_on = [google_alloydb_instance.default] +} diff --git a/mmv1/templates/terraform/examples/alloydb_user_iam.tf.erb b/mmv1/templates/terraform/examples/alloydb_user_iam.tf.erb new file mode 100644 index 000000000000..fd2a84128dd7 --- /dev/null +++ b/mmv1/templates/terraform/examples/alloydb_user_iam.tf.erb @@ -0,0 +1,46 @@ +resource "google_alloydb_instance" "default" { + cluster = google_alloydb_cluster.default.name + instance_id = "<%= ctx[:vars]['alloydb_instance_name'] %>" + instance_type = "PRIMARY" + + depends_on = [google_service_networking_connection.vpc_connection] +} + +resource "google_alloydb_cluster" "default" { + cluster_id = "<%= ctx[:vars]['alloydb_cluster_name'] %>" + location = "us-central1" + network = google_compute_network.default.id + + initial_user { + password = "<%= ctx[:vars]['alloydb_cluster_pass'] %>" + } +} + +data "google_project" "project" {} + +resource "google_compute_network" "default" { + name = "<%= ctx[:vars]['network_name'] %>" +} + +resource "google_compute_global_address" "private_ip_alloc" { + name = "<%= ctx[:vars]['alloydb_cluster_name'] %>" + address_type = "INTERNAL" + purpose = "VPC_PEERING" + prefix_length = 16 + network = google_compute_network.default.id +} + +resource "google_service_networking_connection" "vpc_connection" { + network = google_compute_network.default.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.private_ip_alloc.name] +} + +resource "google_alloydb_user" "<%= ctx[:primary_resource_id] %>" { + cluster = google_alloydb_cluster.default.name + user_id = "<%= ctx[:vars]['alloydb_user_name'] %>" + user_type = "ALLOYDB_IAM_USER" + + database_roles = ["alloydbiamuser"] + depends_on = [google_alloydb_instance.default] +} diff --git a/mmv1/third_party/terraform/services/alloydb/resource_alloydb_user_test.go b/mmv1/third_party/terraform/services/alloydb/resource_alloydb_user_test.go new file mode 100644 index 000000000000..169a4cf5e58c --- /dev/null +++ b/mmv1/third_party/terraform/services/alloydb/resource_alloydb_user_test.go @@ -0,0 +1,253 @@ +package alloydb_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccAlloydbUser_updateRoles_BuiltIn(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckAlloydbUserDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccAlloydbUser_alloydbUserBuiltinExample(context), + }, + { + ResourceName: "google_alloydb_user.user1", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"password"}, + }, + { + Config: testAccAlloydbUser_updateRoles_BuiltIn(context), + }, + { + ResourceName: "google_alloydb_user.user1", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"password"}, + }, + }, + }) +} + +func testAccAlloydbUser_updateRoles_BuiltIn(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_alloydb_instance" "default" { + cluster = google_alloydb_cluster.default.name + instance_id = "tf-test-alloydb-instance%{random_suffix}" + instance_type = "PRIMARY" + + depends_on = [google_service_networking_connection.vpc_connection] +} + +resource "google_alloydb_cluster" "default" { + cluster_id = "tf-test-alloydb-cluster%{random_suffix}" + location = "us-central1" + network = google_compute_network.default.id + + initial_user { + password = "tf_test_cluster_secret%{random_suffix}" + } +} + +data "google_project" "project" {} + +resource "google_compute_network" "default" { + name = "tf-test-alloydb-network%{random_suffix}" +} + +resource "google_compute_global_address" "private_ip_alloc" { + name = "tf-test-alloydb-cluster%{random_suffix}" + address_type = "INTERNAL" + purpose = "VPC_PEERING" + prefix_length = 16 + network = google_compute_network.default.id +} + +resource "google_service_networking_connection" "vpc_connection" { + network = google_compute_network.default.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.private_ip_alloc.name] +} + +resource "google_alloydb_user" "user1" { + cluster = google_alloydb_cluster.default.name + user_id = "user1%{random_suffix}" + user_type = "ALLOYDB_BUILT_IN" + + password = "tf_test_user_secret%{random_suffix}" + database_roles = [] + depends_on = [google_alloydb_instance.default] +}`, context) +} + +func TestAccAlloydbUser_updatePassword_BuiltIn(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckAlloydbUserDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccAlloydbUser_alloydbUserBuiltinExample(context), + }, + { + ResourceName: "google_alloydb_user.user1", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"password"}, + }, + { + Config: testAccAlloydbUser_updatePass_BuiltIn(context), + }, + { + ResourceName: "google_alloydb_user.user1", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"password"}, + }, + }, + }) +} + +func testAccAlloydbUser_updatePass_BuiltIn(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_alloydb_instance" "default" { + cluster = google_alloydb_cluster.default.name + instance_id = "tf-test-alloydb-instance%{random_suffix}" + instance_type = "PRIMARY" + + depends_on = [google_service_networking_connection.vpc_connection] +} + +resource "google_alloydb_cluster" "default" { + cluster_id = "tf-test-alloydb-cluster%{random_suffix}" + location = "us-central1" + network = google_compute_network.default.id + + initial_user { + password = "tf_test_cluster_secret%{random_suffix}" + } +} + +data "google_project" "project" {} + +resource "google_compute_network" "default" { + name = "tf-test-alloydb-network%{random_suffix}" +} + +resource "google_compute_global_address" "private_ip_alloc" { + name = "tf-test-alloydb-cluster%{random_suffix}" + address_type = "INTERNAL" + purpose = "VPC_PEERING" + prefix_length = 16 + network = google_compute_network.default.id +} + +resource "google_service_networking_connection" "vpc_connection" { + network = google_compute_network.default.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.private_ip_alloc.name] +} + +resource "google_alloydb_user" "user1" { + cluster = google_alloydb_cluster.default.name + user_id = "user1%{random_suffix}" + user_type = "ALLOYDB_BUILT_IN" + + password = "tf_test_user_secret%{random_suffix}-foo" + database_roles = ["alloydbsuperuser"] + depends_on = [google_alloydb_instance.default] +}`, context) +} + +func TestAccAlloydbUser_updateRoles_IAM(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckAlloydbUserDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccAlloydbUser_alloydbUserIamExample(context), + }, + { + ResourceName: "google_alloydb_user.user2", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{}, + }, + { + Config: testAccAlloydbUser_updateRoles_Iam(context), + }, + { + ResourceName: "google_alloydb_user.user2", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{}, + }, + }, + }) +} + +func testAccAlloydbUser_updateRoles_Iam(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_alloydb_instance" "default" { + cluster = google_alloydb_cluster.default.name + instance_id = "tf-test-alloydb-instance%{random_suffix}" + instance_type = "PRIMARY" + depends_on = [google_service_networking_connection.vpc_connection] +} +resource "google_alloydb_cluster" "default" { + cluster_id = "tf-test-alloydb-cluster%{random_suffix}" + location = "us-central1" + network = google_compute_network.default.id + initial_user { + password = "tf_test_cluster_secret%{random_suffix}" + } +} +data "google_project" "project" {} +resource "google_compute_network" "default" { + name = "tf-test-alloydb-network%{random_suffix}" +} +resource "google_compute_global_address" "private_ip_alloc" { + name = "tf-test-alloydb-cluster%{random_suffix}" + address_type = "INTERNAL" + purpose = "VPC_PEERING" + prefix_length = 16 + network = google_compute_network.default.id +} +resource "google_service_networking_connection" "vpc_connection" { + network = google_compute_network.default.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.private_ip_alloc.name] +} +resource "google_alloydb_user" "user2" { + cluster = google_alloydb_cluster.default.name + user_id = "user2@foo.com%{random_suffix}" + user_type = "ALLOYDB_IAM_USER" + database_roles = ["alloydbiamuser", "alloydbsuperuser"] + depends_on = [google_alloydb_instance.default] +}`, context) +}