From 38fd9fbc95d5748f419e6efed6db7bf4d0884f21 Mon Sep 17 00:00:00 2001 From: Zhenguo Niu Date: Wed, 25 Nov 2020 20:24:36 +0800 Subject: [PATCH] Add gaussdb_cassandra_instance data source (#690) --- .../gaussdb_cassandra_instance.md | 81 +++++ ..._huaweicloud_gaussdb_cassandra_instance.go | 290 ++++++++++++++++++ ...eicloud_gaussdb_cassandra_instance_test.go | 81 +++++ ...huaweicloud_gaussdb_mysql_instance_test.go | 4 +- huaweicloud/provider.go | 1 + ...eicloud_gaussdb_cassandra_instance_test.go | 14 +- ...huaweicloud_gaussdb_mysql_instance_test.go | 4 +- ...eicloud_gaussdb_opengauss_instance_test.go | 8 +- huaweicloud/util.go | 14 + 9 files changed, 482 insertions(+), 15 deletions(-) create mode 100644 docs/data-sources/gaussdb_cassandra_instance.md create mode 100644 huaweicloud/data_source_huaweicloud_gaussdb_cassandra_instance.go create mode 100644 huaweicloud/data_source_huaweicloud_gaussdb_cassandra_instance_test.go diff --git a/docs/data-sources/gaussdb_cassandra_instance.md b/docs/data-sources/gaussdb_cassandra_instance.md new file mode 100644 index 0000000000..92e39b0c4c --- /dev/null +++ b/docs/data-sources/gaussdb_cassandra_instance.md @@ -0,0 +1,81 @@ +--- +subcategory: "GaussDB" +--- + +# huaweicloud\_gaussdb\_cassandra\_instance + +Use this data source to get available HuaweiCloud gaussdb cassandra instance. + +## Example Usage + +```hcl +data "huaweicloud_gaussdb_cassandra_instance" "this" { + name = "gaussdb-instance" +} +``` + +## Argument Reference + +* `region` - (Optional) The region in which to obtain the instance. If omitted, the provider-level region will be used. + +* `name` - (Optional) Specifies the name of the instance. + +* `vpc_id` - (Optional) Specifies the VPC ID. + +* `subnet_id` - (Optional) Specifies the network ID of a subnet. + + +## Attributes Reference + +* `id` - Indicates the ID of the instance. + +* `status` - Indicates the DB instance status. + +* `mode` - Indicates the instance mode. + +* `flavor` - Indicates the instance specifications. + +* `security_group_id` - Indicates the security group ID. Required if the selected subnet doesn't enable network ACL. + +* `enterprise_project_id` - Indicates the enterprise project id. + +* `db_user_name` - Indicates the default username. + +* `availability_zone` - Indicates the instance availability zone. + +* `port` - Indicates the database port. + +* `node_num` - Indicates the count of the nodes. + +* `volume_size` - Indicates the size of the volume. + +* `private_ips` - Indicates the list of private IP address of the nodes. + +* `datastore` - Indicates the database information. Structure is documented below. + +* `backup_strategy` - Indicates the advanced backup policy. Structure is documented below. + +* `nodes` - Indicates the instance nodes information. Structure is documented below. + +* `tags` - Indicates the key/value tags of the instance. + + +The `datastore` block supports: + +* `engine` - Indicates the database engine. +* `storage_engine` - Indicates the database storage engine. +* `version` - Indicates the database version. + +The `backup_strategy` block supports: + +* `start_time` - Indicates the backup time window. +* `keep_days` - Indicates the number of days to retain the generated + +The `nodes` block contains: + +- `id` - Indicates the node ID. +- `name` - Indicates the node name. +- `private_ip` - Indicates the private IP address of a node. +- `status` - Indicates the node status. +- `support_reduce` - Indicates whether the node support reduce. +- `availability_zone` - Indicates the availability zone where the node resides. diff --git a/huaweicloud/data_source_huaweicloud_gaussdb_cassandra_instance.go b/huaweicloud/data_source_huaweicloud_gaussdb_cassandra_instance.go new file mode 100644 index 0000000000..b6add283f1 --- /dev/null +++ b/huaweicloud/data_source_huaweicloud_gaussdb_cassandra_instance.go @@ -0,0 +1,290 @@ +package huaweicloud + +import ( + "fmt" + "log" + "sort" + "strconv" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + + "github.com/huaweicloud/golangsdk/openstack/common/tags" + "github.com/huaweicloud/golangsdk/openstack/geminidb/v3/instances" +) + +func dataSourceGeminiDBInstance() *schema.Resource { + return &schema.Resource{ + Read: dataSourceGeminiDBInstanceRead, + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + "vpc_id": { + Type: schema.TypeString, + Optional: true, + }, + "subnet_id": { + Type: schema.TypeString, + Optional: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "mode": { + Type: schema.TypeString, + Computed: true, + }, + "security_group_id": { + Type: schema.TypeString, + Computed: true, + }, + "enterprise_project_id": { + Type: schema.TypeString, + Computed: true, + }, + "db_user_name": { + Type: schema.TypeString, + Computed: true, + }, + "availability_zone": { + Type: schema.TypeString, + Computed: true, + }, + "port": { + Type: schema.TypeInt, + Computed: true, + }, + "datastore": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "engine": { + Type: schema.TypeString, + Computed: true, + }, + "storage_engine": { + Type: schema.TypeString, + Computed: true, + }, + "version": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "backup_strategy": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "start_time": { + Type: schema.TypeString, + Computed: true, + }, + "keep_days": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "node_num": { + Type: schema.TypeInt, + Computed: true, + }, + "volume_size": { + Type: schema.TypeInt, + Computed: true, + }, + "private_ips": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "flavor": { + Type: schema.TypeString, + Computed: true, + }, + "nodes": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "private_ip": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "support_reduce": { + Type: schema.TypeBool, + Computed: true, + }, + "availability_zone": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "tags": { + Type: schema.TypeMap, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func dataSourceGeminiDBInstanceRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + region := GetRegion(d, config) + client, err := config.GeminiDBV3Client(region) + if err != nil { + return fmt.Errorf("Error creating HuaweiCloud GaussDB client: %s", err) + } + + listOpts := instances.ListGeminiDBInstanceOpts{ + Name: d.Get("name").(string), + VpcId: d.Get("vpc_id").(string), + SubnetId: d.Get("subnet_id").(string), + } + + pages, err := instances.List(client, listOpts).AllPages() + if err != nil { + return err + } + + allInstances, err := instances.ExtractGeminiDBInstances(pages) + if err != nil { + return fmt.Errorf("Unable to retrieve instances: %s", err) + } + + if allInstances.TotalCount < 1 { + return fmt.Errorf("Your query returned no results. " + + "Please change your search criteria and try again.") + } + + if allInstances.TotalCount > 1 { + return fmt.Errorf("Your query returned more than one result." + + " Please try a more specific search criteria") + } + + instance := allInstances.Instances[0] + + log.Printf("[DEBUG] Retrieved Instance %s: %+v", instance.Id, instance) + d.SetId(instance.Id) + + d.Set("name", instance.Name) + d.Set("region", instance.Region) + d.Set("status", instance.Status) + d.Set("vpc_id", instance.VpcId) + d.Set("subnet_id", instance.SubnetId) + d.Set("security_group_id", instance.SecurityGroupId) + d.Set("enterprise_project_id", instance.EnterpriseProjectId) + d.Set("mode", instance.Mode) + d.Set("db_user_name", instance.DbUserName) + + if dbPort, err := strconv.Atoi(instance.Port); err == nil { + d.Set("port", dbPort) + } + + dbList := make([]map[string]interface{}, 0, 1) + db := map[string]interface{}{ + "engine": instance.DataStore.Type, + "version": instance.DataStore.Version, + "storage_engine": instance.Engine, + } + dbList = append(dbList, db) + d.Set("datastore", dbList) + + specCode := "" + wrongFlavor := "Inconsistent Flavor" + ipsList := []string{} + azList := []string{} + nodesList := make([]map[string]interface{}, 0, 1) + for _, group := range instance.Groups { + for _, Node := range group.Nodes { + node := map[string]interface{}{ + "id": Node.Id, + "name": Node.Name, + "status": Node.Status, + "private_ip": Node.PrivateIp, + "support_reduce": Node.SupportReduce, + "availability_zone": Node.AvailabilityZone, + } + if specCode == "" { + specCode = Node.SpecCode + } else if specCode != Node.SpecCode && specCode != wrongFlavor { + specCode = wrongFlavor + } + nodesList = append(nodesList, node) + azList = append(azList, Node.AvailabilityZone) + // Only return Node private ips which doesn't support reduce + if !Node.SupportReduce { + ipsList = append(ipsList, Node.PrivateIp) + } + } + if volSize, err := strconv.Atoi(group.Volume.Size); err == nil { + d.Set("volume_size", volSize) + } + if specCode != "" { + log.Printf("[DEBUG] Node SpecCode: %s", specCode) + d.Set("flavor", specCode) + } + } + d.Set("nodes", nodesList) + d.Set("private_ips", ipsList) + + //remove duplicate az + azList = removeDuplicateElem(azList) + sort.Strings(azList) + d.Set("availability_zone", strings.Join(azList, ",")) + d.Set("node_num", len(nodesList)) + + backupStrategyList := make([]map[string]interface{}, 0, 1) + backupStrategy := map[string]interface{}{ + "start_time": instance.BackupStrategy.StartTime, + "keep_days": instance.BackupStrategy.KeepDays, + } + backupStrategyList = append(backupStrategyList, backupStrategy) + d.Set("backup_strategy", backupStrategyList) + + //save geminidb tags + resourceTags, err := tags.Get(client, "instances", d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error fetching HuaweiCloud geminidb tags: %s", err) + } + + tagmap := tagsToMap(resourceTags.Tags) + if err := d.Set("tags", tagmap); err != nil { + return fmt.Errorf("Error saving tags for HuaweiCloud geminidb (%s): %s", d.Id(), err) + } + + return nil +} diff --git a/huaweicloud/data_source_huaweicloud_gaussdb_cassandra_instance_test.go b/huaweicloud/data_source_huaweicloud_gaussdb_cassandra_instance_test.go new file mode 100644 index 0000000000..4c384925a3 --- /dev/null +++ b/huaweicloud/data_source_huaweicloud_gaussdb_cassandra_instance_test.go @@ -0,0 +1,81 @@ +package huaweicloud + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccGeminiDBInstanceDataSource_basic(t *testing.T) { + rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccGeminiDBInstanceDataSource_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGeminiDBInstanceDataSourceID("data.huaweicloud_gaussdb_cassandra_instance.test"), + ), + }, + }, + }) +} + +func testAccCheckGeminiDBInstanceDataSourceID(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Can't find GaussDB cassandra instance data source: %s ", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("GaussDB cassandra instance data source ID not set ") + } + + return nil + } +} + +func testAccGeminiDBInstanceDataSource_basic(rName string) string { + return fmt.Sprintf(` +%s + +data "huaweicloud_availability_zones" "test" {} + +data "huaweicloud_networking_secgroup" "test" { + name = "default" +} + +resource "huaweicloud_gaussdb_cassandra_instance" "test" { + name = "%s" + password = "Test@123" + flavor = "geminidb.cassandra.xlarge.4" + volume_size = 100 + vpc_id = huaweicloud_vpc.test.id + subnet_id = huaweicloud_vpc_subnet.test.id + node_num = 4 + + security_group_id = data.huaweicloud_networking_secgroup.test.id + availability_zone = data.huaweicloud_availability_zones.test.names[0] + + backup_strategy { + start_time = "03:00-04:00" + keep_days = 14 + } + + tags = { + foo = "bar" + key = "value" + } +} + +data "huaweicloud_gaussdb_cassandra_instance" "test" { + name = huaweicloud_gaussdb_cassandra_instance.test.name +} +`, testAccVpcConfig_Base(rName), rName) +} diff --git a/huaweicloud/data_source_huaweicloud_gaussdb_mysql_instance_test.go b/huaweicloud/data_source_huaweicloud_gaussdb_mysql_instance_test.go index 2c288ea23f..d5768599bc 100644 --- a/huaweicloud/data_source_huaweicloud_gaussdb_mysql_instance_test.go +++ b/huaweicloud/data_source_huaweicloud_gaussdb_mysql_instance_test.go @@ -55,8 +55,8 @@ resource "huaweicloud_gaussdb_mysql_instance" "test" { name = "%s" password = "Test@123" flavor = "gaussdb.mysql.4xlarge.x86.4" - vpc_id = huaweicloud_vpc_v1.test.id - subnet_id = huaweicloud_vpc_subnet_v1.test.id + vpc_id = huaweicloud_vpc.test.id + subnet_id = huaweicloud_vpc_subnet.test.id security_group_id = data.huaweicloud_networking_secgroup.test.id } diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index ce4dc5ca12..8ab2f68bf2 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -210,6 +210,7 @@ func Provider() terraform.ResourceProvider { "huaweicloud_dms_product": dataSourceDmsProductV1(), "huaweicloud_dms_maintainwindow": dataSourceDmsMaintainWindowV1(), "huaweicloud_enterprise_project": DataSourceEnterpriseProject(), + "huaweicloud_gaussdb_cassandra_instance": dataSourceGeminiDBInstance(), "huaweicloud_gaussdb_mysql_configuration": dataSourceGaussdbMysqlConfigurations(), "huaweicloud_gaussdb_mysql_flavors": dataSourceGaussdbMysqlFlavors(), "huaweicloud_gaussdb_mysql_instance": dataSourceGaussDBMysqlInstance(), diff --git a/huaweicloud/resource_huaweicloud_gaussdb_cassandra_instance_test.go b/huaweicloud/resource_huaweicloud_gaussdb_cassandra_instance_test.go index 08e360636a..419c83dd76 100644 --- a/huaweicloud/resource_huaweicloud_gaussdb_cassandra_instance_test.go +++ b/huaweicloud/resource_huaweicloud_gaussdb_cassandra_instance_test.go @@ -89,19 +89,19 @@ func testAccCheckGeminiDBInstanceExists(n string, instance *instances.GeminiDBIn func testAccVpcConfig_Base(rName string) string { return fmt.Sprintf(` -resource "huaweicloud_vpc_v1" "test" { +resource "huaweicloud_vpc" "test" { name = "%s" cidr = "192.168.0.0/16" } -resource "huaweicloud_vpc_subnet_v1" "test" { +resource "huaweicloud_vpc_subnet" "test" { name = "%s" cidr = "192.168.0.0/16" gateway_ip = "192.168.0.1" primary_dns = "100.125.1.250" secondary_dns = "100.125.21.250" - vpc_id = huaweicloud_vpc_v1.test.id + vpc_id = huaweicloud_vpc.test.id } `, rName, rName) } @@ -112,7 +112,7 @@ func testAccGeminiDBInstanceConfig_basic(rName string) string { data "huaweicloud_availability_zones" "test" {} -data "huaweicloud_networking_secgroup_v2" "test" { +data "huaweicloud_networking_secgroup" "test" { name = "default" } @@ -121,12 +121,12 @@ resource "huaweicloud_gaussdb_cassandra_instance" "test" { password = "Test@123" flavor = "geminidb.cassandra.xlarge.4" volume_size = 100 - vpc_id = huaweicloud_vpc_v1.test.id - subnet_id = huaweicloud_vpc_subnet_v1.test.id + vpc_id = huaweicloud_vpc.test.id + subnet_id = huaweicloud_vpc_subnet.test.id ssl = true node_num = 4 - security_group_id = data.huaweicloud_networking_secgroup_v2.test.id + security_group_id = data.huaweicloud_networking_secgroup.test.id availability_zone = data.huaweicloud_availability_zones.test.names[0] backup_strategy { diff --git a/huaweicloud/resource_huaweicloud_gaussdb_mysql_instance_test.go b/huaweicloud/resource_huaweicloud_gaussdb_mysql_instance_test.go index ef34eeabea..2d78cb01f6 100644 --- a/huaweicloud/resource_huaweicloud_gaussdb_mysql_instance_test.go +++ b/huaweicloud/resource_huaweicloud_gaussdb_mysql_instance_test.go @@ -97,8 +97,8 @@ resource "huaweicloud_gaussdb_mysql_instance" "test" { name = "%s" password = "Test@123" flavor = "gaussdb.mysql.4xlarge.x86.4" - vpc_id = huaweicloud_vpc_v1.test.id - subnet_id = huaweicloud_vpc_subnet_v1.test.id + vpc_id = huaweicloud_vpc.test.id + subnet_id = huaweicloud_vpc_subnet.test.id security_group_id = data.huaweicloud_networking_secgroup.test.id diff --git a/huaweicloud/resource_huaweicloud_gaussdb_opengauss_instance_test.go b/huaweicloud/resource_huaweicloud_gaussdb_opengauss_instance_test.go index a86853fdf1..8188bdd400 100644 --- a/huaweicloud/resource_huaweicloud_gaussdb_opengauss_instance_test.go +++ b/huaweicloud/resource_huaweicloud_gaussdb_opengauss_instance_test.go @@ -87,7 +87,7 @@ func testAccOpenGaussInstanceConfig_basic(rName string) string { return fmt.Sprintf(` %s -data "huaweicloud_networking_secgroup_v2" "test" { +data "huaweicloud_networking_secgroup" "test" { name = "default" } @@ -95,11 +95,11 @@ resource "huaweicloud_gaussdb_opengauss_instance" "test" { name = "%s" password = "Test@123" flavor = "gaussdb.opengauss.ee.dn.m6.2xlarge.8.in" - vpc_id = huaweicloud_vpc_v1.test.id - subnet_id = huaweicloud_vpc_subnet_v1.test.id + vpc_id = huaweicloud_vpc.test.id + subnet_id = huaweicloud_vpc_subnet.test.id availability_zone = "cn-north-4a,cn-north-4a,cn-north-4a" - security_group_id = data.huaweicloud_networking_secgroup_v2.test.id + security_group_id = data.huaweicloud_networking_secgroup.test.id ha { mode = "enterprise" diff --git a/huaweicloud/util.go b/huaweicloud/util.go index a6ddbd50d3..5ca103a3d3 100644 --- a/huaweicloud/util.go +++ b/huaweicloud/util.go @@ -197,3 +197,17 @@ func dataResourceIdHash(ids []string) string { return fmt.Sprintf("%d", hashcode.String(buf.String())) } + +// Remove duplicate elements from slice +func removeDuplicateElem(s []string) []string { + result := []string{} + tmpMap := map[string]byte{} + for _, e := range s { + l := len(tmpMap) + tmpMap[e] = 0 + if len(tmpMap) != l { + result = append(result, e) + } + } + return result +}