From b418c506a592aa6a12f37905cb3248156a81f373 Mon Sep 17 00:00:00 2001 From: Lance52259 Date: Mon, 19 Apr 2021 11:50:00 +0800 Subject: [PATCH 1/2] feat: new bcs instance resource supported --- docs/resources/bcs_instance.md | 352 +++++++++ huaweicloud/config/config.go | 4 + huaweicloud/config/endpoints.go | 4 + huaweicloud/endpoints_test.go | 12 + huaweicloud/provider.go | 1 + .../resource_huaweicloud_bcs_instance.go | 740 ++++++++++++++++++ .../resource_huaweicloud_bcs_instance_test.go | 316 ++++++++ .../openstack/bcs/v2/blockchains/requests.go | 239 ++++++ .../openstack/bcs/v2/blockchains/results.go | 266 +++++++ .../openstack/bcs/v2/blockchains/urls.go | 17 + vendor/modules.txt | 1 + 11 files changed, 1952 insertions(+) create mode 100644 docs/resources/bcs_instance.md create mode 100644 huaweicloud/resource_huaweicloud_bcs_instance.go create mode 100644 huaweicloud/resource_huaweicloud_bcs_instance_test.go create mode 100644 vendor/github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains/requests.go create mode 100644 vendor/github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains/results.go create mode 100644 vendor/github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains/urls.go diff --git a/docs/resources/bcs_instance.md b/docs/resources/bcs_instance.md new file mode 100644 index 0000000000..b07f0b20f7 --- /dev/null +++ b/docs/resources/bcs_instance.md @@ -0,0 +1,352 @@ +--- +subcategory: "Blockchain Service (BCS)" +--- + +# huaweicloud\_bcs\_instance + +## Example Usage + +### Basic Instance + +```hcl +variable "instance_name" {} + +variable "instance_password" {} + +variable "enterprise_project_id" {} + +data "huaweicloud_availability_zones" "test" {} + +data "huaweicloud_cce_cluster" "test" { + ... +} + +resource "huaweicloud_bcs_instance" "test" { + name = var.instance_name + cluster_id = data.huaweicloud_cce_cluster.test.id + consensus = "etcdraft" + edition = 1 + eip_enable = true + enterprise_project_id = var.enterprise_project_id + fabric_version = "2.0" + password = var.instance_password + volume_type = "nfs" + org_disk_size = 100 + security_mechanism = "ECDSA" + orderer_node_num = 1 + delete_storage = true + + peer_orgs { + org_name = "organization01" + count = 2 + } + channels { + name = "channel01" + org_names = [ + "organization01", + ] + } +} +``` + +### Instance With kafka consensus strategy + +```hcl +variable "instance_name" {} + +variable "instance_password" {} + +variable "enterprise_project_id" {} + +variable "database_user_name" {} + +variable "database_password" {} + +data "huaweicloud_availability_zones" "test" {} + +data "huaweicloud_cce_cluster" "test" { + ... +} + +resource "huaweicloud_bcs_instance" "test" { + name = var.instance_name + blockchain_type = "private" + cluster_id = data.huaweicloud_cce_cluster.test.id + consensus = "kafka" + edition = 4 + eip_enable = true + fabric_version = "1.4" + enterprise_project_id = var.enterprise_project_id + password = var.instance_password + volume_type = "efs" + org_disk_size = 500 + database_type = "couchdb" + orderer_node_num = 2 + bandwidth_size = 5 + delete_storage = true + delete_obs = true + + couchdb { + user_name = var.database_user_name + password = var.database_password + } + peer_orgs { + org_name = "organization01" + count = 2 + } + peer_orgs { + org_name = "organization02" + count = 2 + } + channels { + name = "channel01" + org_names = [ + "organization02", + ] + } + channels { + name = "channel02" + org_names = [ + "organization01", + "organization02", + ] + } + sfs_turbo { + share_type = "STANDARD" + type = "efs-ha" + flavor = "sfs.turbo.standard" + availability_zone = data.huaweicloud_availability_zones.test.names[0] + } + kafka { + flavor = "c3.mini" + storage_size = 600 + availability_zone = [ + data.huaweicloud_availability_zones.test.names[0], + data.huaweicloud_availability_zones.test.names[1], + data.huaweicloud_availability_zones.test.names[2], + ] + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `region` - (Optional, String, ForceNew) Specifies the region in which to create the instance. + If omitted, the provider-level region will be used. + Changing this will create a new instance. + +* `name` - (Required, String, ForceNew) Specifies a unique name of the BCS instance. + The name consists of 4 to 24 characters, including letters, digits, chinese charactors and hyphens (-), and the name + cannot start with a hyphen. + Changing this will create a new instance. + +* `edition` - (Required, Int, ForceNew) Specifies Service edition of the BCS instance. + Valid values are `1`, `2` and `4`. + For the specifications corresponding to these three versions, please refer to the [specification table](#jump). + Changing this will create a new instance. + +* `fabric_version` - (Required, String, ForceNew) Specifies version of fabric for the BCS instance. + Valid values are `1.4` and `2.0` + Changing this will create a new instance. + +* `consensus` - (Required, String, ForceNew) Specifies the consensus algorithm used by the BCS instance. + The valid values of fabric 1.4 are `solo`, `kafka` and `SFLIC`, and the valid values of fabric 2.0 are `SFLIC` + and `etcdraft`. + Changing this will create a new instance. + +* `orderer_node_num` - (Required, Int, ForceNew) Specifies the number of peers in the orderer organizaion. + For the number of orderer nodes, please refer to the [specification table](#jump). + Changing this will create a new instance. + +* `cluster_id` - (Required, String, ForceNew) Specifies the ID of the CCE cluster to attach to the BCS instance. + The BCS service needs to exclusively occupy the CCE cluster. Please make sure that the CCE cluster is not occupied + before deploying the BCS service. + Changing this will create a new instance. + +* `enterprise_project_id` - (Required, String, ForceNew) Specifies the ID of the enterprise project that the BCS + instance belong to. + Changing this will create a new instance. + +* `password` - (Required, String, ForceNew) Specifies the Resource access and blockchain management password. + The password consists of 8 to 12 characters and must consist at least three of following: uppercase letters, + lowercase letters, digits, chinese charactors, special charactors(!@$%^-_=+[{}]:,./?). + Changing this will create a new instance. + +* `volume_type` - (Required, String, ForceNew) Specifies the storage volume type to attach to each organization of the + BCS instance. Valid values are `nfs` (SFS) and `efs` (SFS Turbo). + Changing this will create a new instance. + +* `org_disk_size` - (Required, Int, ForceNew) Specifies the storage capacity of peer organization. + Changing this will create a new instance. + * The minimum storage capacity of `efs` volume type is 500GB. + + The specifications are as follows when `volume_type` is `nfs`: + * The minimum storage capacity of basic edition is 40 GB. + * The minimum storage capacity of enterprise and professional edition is 100 GB. + * The minimum storage capacity of platinum edition is 500 GB. + +* `blockchain_type` - (Optional, String, ForceNew) Specifies the blockchain type of the BCS instance. + Valid values are `private` and `union`. Default is `private`. + Changing this will create a new instance. + +* `channels` - (Optional, List, ForceNew) Specifies an array of one or more channels to attach to the BCS + instance. For the maximum number of channels, please refer to the specification table. + If omitted, the bcs instance will create a `channels` named `channel` by default. + Changing this will create a new instance. + The channels object structure is documented below. + +* `couchdb` - (Optional, List, ForceNew) Specifies the NoSQL database used by BCS instance. + If omitted, the bcs instance will create a `goleveldb`(File Database) database by default. + Changing this will create a new instance. + The couchdb object structure is documented below. + +* `delete_storage` - (Optional, Bool) Specified whether to delete the associated SFS resources when deleting BCS + instance. Default is false. + +* `delete_obs` - (Optional, Bool) Specified whether to delete the associated OBS bucket when deleting BCS instance. + `delete_obs` is used to delete the OBS created by the BCS instance of the Kafka consensus strategy. Default is false. + +* `eip_enable` - (Optional, Bool, ForceNew) Specifies whether to use the EIP of the CCE to bind the BCS instance. + Changing this will create a new instance. Defalut is true. + * `true` means an EIP bound to the cluster will be used as the blockchain network access address. + If the cluster is not bound with any EIP, bind an EIP to the cluster first. + Please make sure that the cluster is bound to EIP. + * `false` means a private address of the cluster will be used ad the blockchain network access address to ensure that + the application can communicate with the internal network of the cluster. + +* `kafka` - (Optional, List, ForceNew) Specifies the kafka configuration for the BCS instance. + Changing this will create a new instance. + The kafka object structure is documented below. + +* `peer_orgs` - (Optional, List, ForceNew) Specifies an array of one or more Peer organizations to attach to the BCS + instance. Changing this will create a new instance. + If omitted, the bcs instance will create a `peer_orgs` named `organization` by default and the node count is 2. + The peer_orgs object structure is documented below. + +* `restful_api_support` - (Optional, Bool, ForceNew) Specified whether to add RESTful API support. + Changing this will create a new instance. + +* `sfs_turbo` - (Optional, List, ForceNew) Specifies the information about the SFS Turbo file system. + Changing this will create a new instance. + The sfs_turbo object structure is documented below. + +* `security_mechanism` - (Optional, String, ForceNew) Specifies the secutity mechanism used by the BCS instance. + Valid values are `ECDSA` and `SM2`(Chinese cryptographic algorithms, The basic and professional don't support this + algorithm). Default is `ECDSA`. + Changing this will create a new instance. + +* `tc3_need` - (Optional, Bool, ForceNew) Specified whether to add Trusted computing platform. + Changing this will create a new instance. + +The `peer_orgs` block supports: + +* `org_name` - (Required, String, ForceNew) Specifies the name of the peer organization. + Changing this creates a new instance. + +* `count` - (Required, Int, ForceNew) Specifies the number of peers in organization. + For the number of organization nodes, please refer to the [specification table](#jump). + Changing this creates a new instance. + +The `channels` block supports: + +* `name` - (Required, String, ForceNew) Specifies the name of the channel. + Changing this creates a new instance. + +* `org_name` - (Optional, List, ForceNew) Specifies the name of the peer organization. + Changing this creates a new instance. + +The `couchdb` block supports: + +* `user_name` - (Required, String, ForceNew) Specifies the user name of the couch datebase. + Changing this creates a new instance. + +* `password` - (Required, String, ForceNew) Specifies the password of the couch datebase. + The password consists of 8 to 26 characters and must consist at least three of following: uppercase letters, + lowercase letters, digits, special charactors(!@$%^-_=+[{}]:,./?). + Changing this creates a new instance. + +The `sfs_turbo` block supports: + +* `availability_zone` - (Optional, String, ForceNew) Specifies the availability zone in which to create the SFS turbo. + Please following [reference](https://developer.huaweicloud.com/en-us/endpoint/?all) for the values. + Changing this creates a new instance. + +* `flavor` - (Optional, String, ForceNew) Specifies the flavor of SFS turbo. + Changing this creates a new instance. + +* `share_type` - (Optional, String, ForceNew) Specifies the share type of the SFS turbo. + Changing this creates a new instance. + +* `type` - (Optional, String, ForceNew) Specifies the type of SFS turbo. + Changing this creates a new instance. + +The `block_info` block supports: + +* `transaction_quantity` - (Optional, Int, ForceNew) Specifies the number of transactions included in the block. + The defalt value is 500. + Changing this creates a new instance. + +* `block_size` - (Optional, Int, ForceNew) Specifies the volume of the block, the unit is MB. + The default value is 2. + Changing this creates a new instance. + +* `generation_interval` - (Optional, Int, ForceNew) Specifies the block generation time, the unit is second. + The defalt value is 2. + Changing this creates a new instance. + +The `kafka` block supports: + +* `availability_zone` - (Optional, List, ForceNew) Specifies the availability zone in which to create the kafka. + The list must contain one or more than three availability zone. + Please following [reference](https://developer.huaweicloud.com/en-us/endpoint/?all) for the values. + Changing this creates a new instance. + +* `flavor` - (Optional, String, ForceNew) Specifies the kafka flavor type. + Changing this creates a new instance. + * `c3.mini` : Mini type, the reference bandwidth is 100MB/s. + * `c3.small.2` : Small type, the reference bandwidth is 300MB/s. + * `c3.middle.2` : Middle type, the reference bandwidth is 600MB/s. + * `c3.high.2` : High type, the reference bandwidth is 1200MB/s. + +* `storage_size` - (Optional, Int, ForceNew) Specifies the kafka storage capacity. + The storage capacity must be an integral multiple of 100 and the maximum is 90000GB. + Changing this creates a new instance. + * The minimum storage capacity of mini type is 600GB. + * The minimum storage capacity of small type is 1200GB. + * The minimum storage capacity of middle type is 2400GB. + * The minimum storage capacity of high type is 4800GB. + +Specification Table of BCS instance + | Edition | Version Type | Organizations(Max) | Peers(Max) | Orderers(Max) | Channels(Max) | + | ------- | ------------ | ------------------ | ---------- | ------------- | ------------- | + | Basic | 1 | 1 | 2 | 1 | 1 | + | Professional | 4 | 2 | 2 | 3 | 2 | + | Enterprise | 2 | 5 | 2 | 4 | 4 | + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - A resource ID in UUID format. +* `status` - The status of the BCS instance. +* `version` - The service verison of the BCS instance. +* `purchase_type` - The deployment type of the BCS instance. +* `cross_region_support` - Whether the BCS instance is deployed across regions. +* `rollback_support` - Whether rollback is supported when the BCS service fails to br upgraded. +* `old_service_version` - The version of an old BCS service. +* `agent_portal_address` - The agent addresses and port numbers on the user data plane of the BCS service. +* `peer_orgs/pvc_name` - The name of the PersistenetVolumeClaim (PVC) used by the peer. +* `peer_orgs/status` - The peer status. The value contains `IsCreating`, `IsUpgrading`, `Adding/IsScaling`, + `Isdeleting`, `Normal`, `AbNormal` and `Unknown`. +* `peer_orgs/status_detail` - The peer status in the format like `1/1`. The denominator is the total number of peers in + the organization, and the numerator is the number of normal peers. +* `peer_orgs/address/domain_port` - The domain name address. +* `peer_orgs/address/ip_port` - The IP address. +* `kafka/name` - The Kafka instance name. + +## Timeouts +This resource provides the following timeouts configuration options: +- `create` - Default is 90 minute. +- `delete` - Default is 30 minute. diff --git a/huaweicloud/config/config.go b/huaweicloud/config/config.go index c1c1c408d5..4097c35513 100644 --- a/huaweicloud/config/config.go +++ b/huaweicloud/config/config.go @@ -719,6 +719,10 @@ func (c *Config) ApiGatewayV1Client(region string) (*golangsdk.ServiceClient, er return c.NewServiceClient("apig", region) } +func (c *Config) BcsV2Client(region string) (*golangsdk.ServiceClient, error) { + return c.NewServiceClient("bcs", region) +} + func (c *Config) DcsV1Client(region string) (*golangsdk.ServiceClient, error) { return c.NewServiceClient("dcsv1", region) } diff --git a/huaweicloud/config/endpoints.go b/huaweicloud/config/endpoints.go index 95d79d1303..8008b4b686 100644 --- a/huaweicloud/config/endpoints.go +++ b/huaweicloud/config/endpoints.go @@ -287,6 +287,10 @@ var allServiceCatalog = map[string]ServiceCatalog{ ResourceBase: "apigw", WithOutProjectID: true, }, + "bcs": { + Name: "bcs", + Version: "v2", + }, "dcsv1": { Name: "dcs", Version: "v1.0", diff --git a/huaweicloud/endpoints_test.go b/huaweicloud/endpoints_test.go index 386561fd3d..fc03efe13a 100644 --- a/huaweicloud/endpoints_test.go +++ b/huaweicloud/endpoints_test.go @@ -314,6 +314,18 @@ func TestAccServiceEndpoints_Application(t *testing.T) { } t.Logf("API-GW endpoint:\t %s", actualURL) + // test the endpoint of BCS v2 service + serviceClient, err = config.BcsV2Client(HW_REGION_NAME) + if err != nil { + t.Fatalf("Error creating HuaweiCloud BCS v2 client: %s", err) + } + expectedURL = fmt.Sprintf("https://bcs.%s.%s/v2/%s/", HW_REGION_NAME, config.Cloud, config.TenantID) + actualURL = serviceClient.ResourceBaseURL() + if actualURL != expectedURL { + t.Fatalf("BCS v2 endpoint: expected %s but got %s", green(expectedURL), yellow(actualURL)) + } + t.Logf("BCS v2 endpoint:\t %s", actualURL) + // test the endpoint of DCS v1 service serviceClient, err = config.DcsV1Client(HW_REGION_NAME) if err != nil { diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index bd2480607e..f3616ba779 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -355,6 +355,7 @@ func Provider() terraform.ResourceProvider { "huaweicloud_as_configuration": ResourceASConfiguration(), "huaweicloud_as_group": ResourceASGroup(), "huaweicloud_as_policy": ResourceASPolicy(), + "huaweicloud_bcs_instance": resourceBCSInstanceV2(), "huaweicloud_cce_cluster": ResourceCCEClusterV3(), "huaweicloud_cce_node": ResourceCCENodeV3(), "huaweicloud_cce_addon": ResourceCCEAddonV3(), diff --git a/huaweicloud/resource_huaweicloud_bcs_instance.go b/huaweicloud/resource_huaweicloud_bcs_instance.go new file mode 100644 index 0000000000..2fc0abc6ef --- /dev/null +++ b/huaweicloud/resource_huaweicloud_bcs_instance.go @@ -0,0 +1,740 @@ +package huaweicloud + +import ( + "bytes" + "fmt" + "log" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/hashcode" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains" + "github.com/huaweicloud/golangsdk/openstack/cce/v3/clusters" + "github.com/huaweicloud/golangsdk/openstack/dms/v1/instances" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" +) + +func resourceBCSInstanceV2() *schema.Resource { + return &schema.Resource{ + Create: resourceBCSInstanceV2Create, + Read: resourceBCSInstanceV2Read, + Update: resourceBCSInstanceV2Update, + Delete: resourceBCSInstanceV2Delete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(90 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "orderer_node_num": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "password": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + ForceNew: true, + }, + "consensus": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "edition": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "enterprise_project_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "fabric_version": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "volume_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "org_disk_size": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "blockchain_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "security_mechanism": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "database_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "eip_enable": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + "cluster_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "peer_orgs": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "org_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "count": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "pvc_name": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "status_detail": { + Type: schema.TypeString, + Computed: true, + }, + "address": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "domain_port": { + Type: schema.TypeString, + Required: true, + }, + "ip_port": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + }, + }, + Set: resourcePeerOrgsHash, + }, + "channels": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "org_names": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + Set: resourceChannelsHash, + }, + "couchdb": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "password": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, + }, + }, + }, + }, + "sfs_turbo": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "availability_zone": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "flavor": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "share_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + }, + }, + "block_info": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "transaction_quantity": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + "block_size": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + "generation_interval": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + }, + }, + }, + "kafka": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "availability_zone": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "flavor": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "storage_size": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "tc3_need": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, + "restful_api_support": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, + "delete_obs": { + Type: schema.TypeBool, + Optional: true, + }, + "delete_storage": { + Type: schema.TypeBool, + Optional: true, + }, + "cluster_type": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "version": { + Type: schema.TypeString, + Computed: true, + }, + "purchase_type": { + Type: schema.TypeString, + Computed: true, + }, + "cross_region_support": { + Type: schema.TypeBool, + Computed: true, + }, + "rollback_support": { + Type: schema.TypeBool, + Computed: true, + }, + "old_service_version": { + Type: schema.TypeString, + Computed: true, + }, + "agent_portal_address": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func resourceBCSInstanceV2Create(d *schema.ResourceData, meta interface{}) error { + var FALSE bool = false + config := meta.(*config.Config) + client, err := config.BcsV2Client(GetRegion(d, config)) + if err != nil { + return fmt.Errorf("Error creating HuaweiCloud Blockchain client: %s", err) + } + createOpts := blockchains.CreateOpts{ + CreateNewCluster: &FALSE, + Name: d.Get("name").(string), + VersionType: d.Get("edition").(int), + FabricVersion: d.Get("fabric_version").(string), + BlockChainType: d.Get("blockchain_type").(string), + Consensus: d.Get("consensus").(string), + EIPEnable: d.Get("eip_enable").(bool), + SignAlgorithm: d.Get("security_mechanism").(string), + VolumeType: d.Get("volume_type").(string), + OrgDiskSize: d.Get("org_disk_size").(int), + DatabaseType: d.Get("database_type").(string), + Password: d.Get("password").(string), + OrdererNodeNumber: d.Get("orderer_node_num").(int), + TC3Need: d.Get("tc3_need").(bool), + RestfulAPISupport: d.Get("restful_api_support").(bool), + EnterpriseProjectId: GetEnterpriseProjectID(d, config), + PeerOrgs: resourceOrgPeer(d), + Channels: resourceChannelInfo(d), + CouchDBInfo: resourceCouchDBInfo(d), + SFSTurbo: resourceTurboInfo(d), + Block: resourceBlockInfo(d), + Kafka: resourceKafkaCreateInfo(d), + } + + if v, err := resourceClusterInfo(config, d.Get("cluster_id").(string), GetRegion(d, config)); err != nil { + return fmt.Errorf("Get cluster information failed: %s ", err) + } else { + createOpts.ClusterType = "cce" + createOpts.CCEClusterInfo = v + } + + res, err := blockchains.Create(client, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating Blockchain instance: %s ", err) + } + + d.SetId(res.ID) + instanceID := d.Id() + stateConf := &resource.StateChangeConf{ + Pending: []string{"IsCreating"}, + Target: []string{"Normal"}, + Refresh: blockchainStateRefreshFunc(client, instanceID), + Timeout: d.Timeout(schema.TimeoutCreate), + Delay: 150 * time.Second, + PollInterval: 15 * time.Second, + } + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for blockchain instance (%s) status to Normal: %s ", instanceID, err) + } + + return resourceBCSInstanceV2Read(d, meta) +} + +func resourceBCSInstanceV2Read(d *schema.ResourceData, meta interface{}) error { + config := meta.(*config.Config) + client, err := config.BcsV2Client(GetRegion(d, config)) + if err != nil { + return fmt.Errorf("Error creating HuaweiCloud Blockchain client: %s", err) + } + + instanceID := d.Id() + instance, err := blockchains.Get(client, instanceID).Extract() + if err != nil { + return fmt.Errorf("Error getting Blockchain instance (%s): %s", instanceID, err) + } + log.Printf("[DEBUG] Retrieved Blockchain instance %s: %#v", instanceID, instance) + + d.Set("name", instance.Basic.Name) + d.Set("edition", instance.Basic.VersionType) + d.Set("blockchain_type", instance.Basic.ServiceType) + d.Set("consensus", instance.Basic.Consensus) + d.Set("security_mechanism", instance.Basic.SignAlgorithm) + d.Set("database_type", instance.Basic.DatabaseType) + d.Set("restful_api_support", instance.Basic.IsSupportRestful) + d.Set("status", instance.Basic.Status) + d.Set("tc3_need", instance.CouchDB != (blockchains.CouchDB{})) + d.Set("cluster_type", instance.Basic.ClusterType) + d.Set("cluster_id", instance.Basic.ClusterID) + d.Set("version", instance.Basic.Version) + d.Set("purchase_type", instance.Basic.PurchaseType) + d.Set("cross_region_support", instance.Basic.IsCrossRegion) + d.Set("rollback_support", instance.Basic.IsSupportRollback) + d.Set("old_service_version", instance.Basic.OldServiceVersion) + + portalAddrs := make([]string, len(instance.Basic.AgentPortalAddress)) + for i, v := range instance.Basic.AgentPortalAddress { + portalAddrs[i] = v + } + d.Set("agent_portal_address", portalAddrs) + + channelList := make([]map[string]interface{}, len(instance.Channels)) + for i, v := range instance.Channels { + channel := map[string]interface{}{ + "name": v.Name, + "org_names": v.OrgNames, + } + channelList[i] = channel + } + d.Set("channels", channelList) + + peerList := make([]map[string]interface{}, len(instance.Peer)) + for i, org := range instance.Peer { + address := make([]map[string]interface{}, len(org.Address)) + for j, v := range org.Address { + addr := map[string]interface{}{ + "domain_port": v.DomainPort, + "ip_port": v.IPPort, + } + address[j] = addr + } + peerList[i] = map[string]interface{}{ + "org_name": org.Name, + "count": org.NodeCount, + "status": org.Status, + "status_detail": org.StatusDetail, + "pvc_name": org.PVCName, + "address": address, + } + } + d.Set("peer_orgs", peerList) + + if instance.CouchDB != (blockchains.CouchDB{}) { + couchDBList := make([]map[string]interface{}, 1) + info := map[string]interface{}{ + "user_name": instance.CouchDB.User, + "password": d.Get("couchdb.0.password"), + } + couchDBList[0] = info + d.Set("couchdb", couchDBList) + } + + if instance.Basic.Consensus == "kafka" { + kafkaList := make([]map[string]interface{}, 1) + info := map[string]interface{}{ + "name": instance.DMSKafka.Name, + "flavor": d.Get("kafka.0.flavor"), + "storage_size": d.Get("kafka.0.storage_size"), + "availability_zone": d.Get("kafka.0.availability_zone"), + } + kafkaList[0] = info + d.Set("kafka", kafkaList) + } + + return nil +} + +func resourceBCSInstanceV2Update(d *schema.ResourceData, meta interface{}) error { + //Since delete_obs and delete_storage involve updates but contains cloud service modifications, + //an empty udpate function will be set and only the read method will be called + return nil +} + +func resourceBCSInstanceV2Delete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*config.Config) + bcsClient, err := config.BcsV2Client(GetRegion(d, config)) + if err != nil { + return fmt.Errorf("Error creating HuaweiCloud Blockchain client: %s", err) + } + if d.Get("consensus").(string) == "kafka" { + dmsClient, err := config.DmsV2Client(GetRegion(d, config)) + if err != nil { + return fmt.Errorf("Error creating HuaweiCloud Kafka client: %s", err) + } + kafkaName := d.Get("kafka.0.name").(string) + listOpts := instances.ListDmsInstanceOpts{ + Engine: "kafka", + Name: kafkaName, + } + pages, err := instances.List(dmsClient, listOpts).AllPages() + if err != nil { + return fmt.Errorf("Error getting kafka instance in queue (%s): %s", kafkaName, err) + } + res, err := instances.ExtractDmsInstances(pages) + if err != nil { + return fmt.Errorf("Error quering HuaweiCloud kafka instances: %s", err) + } + if len(res.Instances) < 1 { + return fmt.Errorf("Error quering kafka, returned no results") + } + if len(res.Instances) > 1 { + return fmt.Errorf("Error quering kafka, returned more than one result") + } + kafkaID := res.Instances[0].InstanceID + if r := instances.Delete(dmsClient, kafkaID); r.Result.Err != nil { + return fmt.Errorf("Error deleting kafka instance (%s): %s ", kafkaID, r.Result.Err) + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"DELETING", "RUNNING"}, + Target: []string{"DELETED"}, + Refresh: DmsInstancesV1StateRefreshFunc(dmsClient, kafkaID), + Timeout: d.Timeout(schema.TimeoutDelete), + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + if _, err = stateConf.WaitForState(); err != nil { + return fmt.Errorf("Error waiting for instance (%s) to delete: %s", kafkaID, err) + } + } + + blockchainID := d.Id() + deleteOpts := blockchains.DeleteOpts{} + if v, ok := d.GetOk("delete_obs"); ok { + deleteOpts.IsDeleteOBS = v.(bool) + } + if v, ok := d.GetOk("delete_storage"); ok { + deleteOpts.IsDeleteStorage = v.(bool) + } + + if err := blockchains.Delete(bcsClient, deleteOpts, blockchainID).Extract(); err != nil { + return fmt.Errorf("Error deleting HuaweiCloud Blockchain instance (%s): %s", blockchainID, err) + } + stateConf := &resource.StateChangeConf{ + Pending: []string{"IsDeleting"}, + Target: []string{"IsDeleted"}, + Refresh: blockchainStateRefreshFunc(bcsClient, blockchainID), + Timeout: d.Timeout(schema.TimeoutCreate), + Delay: 15 * time.Second, + PollInterval: 10 * time.Second, + } + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for blockchain instance (%s) status to deleted: %s ", blockchainID, err) + } + d.SetId("") + + return nil +} + +func blockchainStateRefreshFunc(client *golangsdk.ServiceClient, instanceID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + instance, err := blockchains.Get(client, instanceID).Extract() + if err != nil { + if _, ok := err.(golangsdk.ErrDefault400); ok { + return instance, "IsDeleted", nil + } + return nil, "FOUND ERROR", err + } + if instance.Basic.ID == "" { + return instance, "IsDeleted", nil + } + if instance.Basic.ProcessStatus != "" { + return instance, instance.Basic.ProcessStatus, nil + } + + return instance, instance.Basic.Status, nil + } +} + +func resourceClusterInfo(config *config.Config, clusterID, region string) (*blockchains.CCEClusterInfo, error) { + clusterInfo := new(blockchains.CCEClusterInfo) + + client, err := config.CceV3Client(region) + if err != nil { + return clusterInfo, fmt.Errorf("Error creating HuaweiCloud CCE client: %s", err) + } + n, err := clusters.Get(client, clusterID).Extract() + if err != nil { + return clusterInfo, fmt.Errorf("Error retrieving HuaweiCloud CCE: %s", err) + } + clusterInfo.ID = clusterID + clusterInfo.Name = n.Metadata.Name + + return clusterInfo, nil +} + +func resourceOrgPeer(d *schema.ResourceData) []blockchains.PeerOrg { + infoRaw := d.Get("peer_orgs") + nodeList := make([]blockchains.PeerOrg, infoRaw.(*schema.Set).Len()) + for i, v := range infoRaw.(*schema.Set).List() { + var peerOrgInfo blockchains.PeerOrg + peerOrgInfo.Name = v.(map[string]interface{})["org_name"].(string) + peerOrgInfo.NodeCount = v.(map[string]interface{})["count"].(int) + nodeList[i] = peerOrgInfo + } + return nodeList +} + +func resourceChannelInfo(d *schema.ResourceData) []blockchains.ChannelInfo { + chRaw := d.Get("channels") + channelList := make([]blockchains.ChannelInfo, 0, chRaw.(*schema.Set).Len()) + + for _, v := range chRaw.(*schema.Set).List() { + var channelInfo blockchains.ChannelInfo + channelInfo.Name = v.(map[string]interface{})["name"].(string) + orgNameList := make([]string, len(v.(map[string]interface{})["org_names"].([]interface{}))) + for j, org := range v.(map[string]interface{})["org_names"].([]interface{}) { + orgNameList[j] = org.(string) + } + channelInfo.OrgNames = orgNameList + channelList = append(channelList, channelInfo) + } + return channelList +} + +func resourceCouchDBInfo(d *schema.ResourceData) *blockchains.CouchDBInfo { + var couchDBInfo *blockchains.CouchDBInfo + var infoRaw []interface{} + if v, ok := d.GetOk("couchdb"); ok { + infoRaw = v.([]interface{}) + } + if len(infoRaw) == 1 { + couchDBInfo = new(blockchains.CouchDBInfo) + couchDBInfo.UserName = infoRaw[0].(map[string]interface{})["user_name"].(string) + couchDBInfo.Password = infoRaw[0].(map[string]interface{})["password"].(string) + } + return couchDBInfo +} + +func resourceTurboInfo(d *schema.ResourceData) *blockchains.SFSTurbo { + var turboInfo *blockchains.SFSTurbo + var infoRaw []interface{} + if v, ok := d.GetOk("sfs_turbo"); ok { + infoRaw = v.([]interface{}) + } + if len(infoRaw) == 1 { + turboInfo = new(blockchains.SFSTurbo) + turboInfo.ShareType = infoRaw[0].(map[string]interface{})["share_type"].(string) + turboInfo.Type = infoRaw[0].(map[string]interface{})["type"].(string) + turboInfo.AvailabilityZone = infoRaw[0].(map[string]interface{})["availability_zone"].(string) + turboInfo.Flavor = infoRaw[0].(map[string]interface{})["flavor"].(string) + } + return turboInfo +} + +func resourceBlockInfo(d *schema.ResourceData) *blockchains.BlockInfo { + var blockInfo *blockchains.BlockInfo + var infoRaw []interface{} + if v, ok := d.GetOk("block_info"); ok { + infoRaw = v.([]interface{}) + } + if len(infoRaw) == 1 { + blockInfo = new(blockchains.BlockInfo) + blockInfo.BatchTimeout = infoRaw[0].(map[string]interface{})["generation_interval"].(int) + blockInfo.MaxMessageCount = infoRaw[0].(map[string]interface{})["transaction_quantity"].(int) + blockInfo.PreferredMaxbytes = infoRaw[0].(map[string]interface{})["block_size"].(int) + } + return blockInfo +} + +func resourceKafkaCreateInfo(d *schema.ResourceData) *blockchains.KafkaInfo { + var createInfo *blockchains.KafkaInfo + var buffer bytes.Buffer + var infoRaw []interface{} + + buffer.WriteString("dms.instance.kafka.cluster.") + if v, ok := d.GetOk("kafka"); ok { + infoRaw = v.([]interface{}) + } + if len(infoRaw) == 1 { + createInfo = new(blockchains.KafkaInfo) + createInfo.Storage = infoRaw[0].(map[string]interface{})["storage_size"].(int) + buffer.WriteString(infoRaw[0].(map[string]interface{})["flavor"].(string)) + createInfo.Flavor = buffer.String() + sliceAZ := make([]string, len(infoRaw[0].(map[string]interface{})["availability_zone"].([]interface{}))) + for i, v := range infoRaw[0].(map[string]interface{})["availability_zone"].([]interface{}) { + sliceAZ[i] = v.(string) + } + createInfo.AvailabilityZone = strings.Join(sliceAZ, ",") + } + return createInfo +} + +func resourceChannelsHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + + if m["name"] != nil { + buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) + } + + return hashcode.String(buf.String()) +} + +func resourcePeerOrgsHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + + if m["org_name"] != nil { + buf.WriteString(fmt.Sprintf("%s-", m["org_name"].(string))) + } + + return hashcode.String(buf.String()) +} diff --git a/huaweicloud/resource_huaweicloud_bcs_instance_test.go b/huaweicloud/resource_huaweicloud_bcs_instance_test.go new file mode 100644 index 0000000000..0c8a013245 --- /dev/null +++ b/huaweicloud/resource_huaweicloud_bcs_instance_test.go @@ -0,0 +1,316 @@ +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" + + "github.com/huaweicloud/golangsdk" + "github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" +) + +func TestAccBCSV2Instance_basic(t *testing.T) { + var instance blockchains.BCSInstance + rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + password := fmt.Sprintf("%s%s%d", acctest.RandString(5), acctest.RandStringFromCharSet(3, "!@$%^-_=+[{}]:,./?"), + acctest.RandIntRange(1, 3)) + resourceName := "huaweicloud_bcs_instance.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckEpsID(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckBCSInstanceV2Destroy(), + Steps: []resource.TestStep{ + { + Config: testBCSInstanceV2_basic(rName, password), + Check: resource.ComposeTestCheckFunc( + testAccCheckBCSInstanceV2Exists(resourceName, &instance), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "edition", "4"), + resource.TestCheckResourceAttr(resourceName, "consensus", "etcdraft"), + resource.TestCheckResourceAttr(resourceName, "fabric_version", "2.0"), + resource.TestCheckResourceAttr(resourceName, "blockchain_type", "private"), + resource.TestCheckResourceAttr(resourceName, "enterprise_project_id", HW_ENTERPRISE_PROJECT_ID_TEST), + resource.TestCheckResourceAttr(resourceName, "volume_type", "nfs"), + resource.TestCheckResourceAttr(resourceName, "org_disk_size", "100"), + resource.TestCheckResourceAttr(resourceName, "security_mechanism", "ECDSA"), + resource.TestCheckResourceAttr(resourceName, "database_type", "goleveldb"), + resource.TestCheckResourceAttr(resourceName, "orderer_node_num", "3"), + resource.TestCheckResourceAttr(resourceName, "channels.#", "1"), + resource.TestCheckResourceAttr(resourceName, "peer_orgs.#", "1"), + ), + }, + }, + }) +} + +func TestAccBCSV2Instance_kafka(t *testing.T) { + var instance blockchains.BCSInstance + rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + password := fmt.Sprintf("%s%s%d", acctest.RandString(5), acctest.RandStringFromCharSet(3, "!@$%^-_=+[{}]:,./?"), + acctest.RandIntRange(1, 3)) + resourceName := "huaweicloud_bcs_instance.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckEpsID(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckBCSInstanceV2Destroy(), + Steps: []resource.TestStep{ + { + Config: testBCSInstanceV2_kafka(rName, password), + Check: resource.ComposeTestCheckFunc( + testAccCheckBCSInstanceV2Exists(resourceName, &instance), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "edition", "4"), + resource.TestCheckResourceAttr(resourceName, "consensus", "kafka"), + resource.TestCheckResourceAttr(resourceName, "fabric_version", "1.4"), + resource.TestCheckResourceAttr(resourceName, "blockchain_type", "private"), + resource.TestCheckResourceAttr(resourceName, "enterprise_project_id", HW_ENTERPRISE_PROJECT_ID_TEST), + resource.TestCheckResourceAttr(resourceName, "volume_type", "efs"), + resource.TestCheckResourceAttr(resourceName, "org_disk_size", "500"), + resource.TestCheckResourceAttr(resourceName, "security_mechanism", "ECDSA"), + resource.TestCheckResourceAttr(resourceName, "database_type", "couchdb"), + resource.TestCheckResourceAttr(resourceName, "orderer_node_num", "2"), + resource.TestCheckResourceAttr(resourceName, "couchdb.0.user_name", "Administrator"), + resource.TestCheckResourceAttr(resourceName, "channels.#", "2"), + resource.TestCheckResourceAttr(resourceName, "peer_orgs.#", "2"), + resource.TestCheckResourceAttr(resourceName, "sfs_turbo.0.share_type", "STANDARD"), + resource.TestCheckResourceAttr(resourceName, "sfs_turbo.0.type", "efs-ha"), + resource.TestCheckResourceAttr(resourceName, "sfs_turbo.0.flavor", "sfs.turbo.standard"), + resource.TestCheckResourceAttrSet(resourceName, "sfs_turbo.0.availability_zone"), + resource.TestCheckResourceAttr(resourceName, "kafka.0.flavor", "c3.mini"), + resource.TestCheckResourceAttr(resourceName, "kafka.0.storage_size", "600"), + resource.TestCheckResourceAttr(resourceName, "kafka.0.availability_zone.#", "1"), + ), + }, + }, + }) +} + +func testAccCheckBCSInstanceV2Destroy() resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*config.Config) + client, err := config.BcsV2Client(HW_REGION_NAME) + if err != nil { + return fmt.Errorf("Error creating huaweicloud BCS client: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "huaweicloud_bcs_instance" { + continue + } + + id := rs.Primary.ID + instance, err := blockchains.Get(client, id).Extract() + if err != nil { + if _, ok := err.(golangsdk.ErrDefault400); ok { + return nil + } + return err + } + if instance.Basic.ID != "" { + return fmt.Errorf("%s (%s) still exists", rs.Type, id) + } + } + return nil + } +} + +func testAccCheckBCSInstanceV2Exists(name string, instance *blockchains.BCSInstance) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + id := rs.Primary.ID + if id == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*config.Config) + client, err := config.BcsV2Client(HW_REGION_NAME) + if err != nil { + return fmt.Errorf("Error creating huaweicloud BCS client: %s", err) + } + + found, err := blockchains.Get(client, id).Extract() + if err != nil { + return fmt.Errorf("Error checking %s exist, err=%s", name, err) + } + if found.Basic.ID == "" { + return fmt.Errorf("resource %s does not exist", name) + } + + instance = found + return nil + } +} + +func testBCSInstanceV2_base(rName string) string { + return fmt.Sprintf(` +data "huaweicloud_availability_zones" "test" {} + +resource "huaweicloud_vpc" "test" { + name = "%s" + cidr = "192.168.0.0/24" +} + +resource "huaweicloud_vpc_subnet" "test" { + name = "%s" + cidr = "192.168.0.0/24" + gateway_ip = "192.168.0.1" + primary_dns = "100.125.1.250" + secondary_dns = "100.125.129.250" + vpc_id = huaweicloud_vpc.test.id +} + +resource "huaweicloud_cce_cluster" "test" { + name = "%s" + flavor_id = "cce.s2.small" + vpc_id = huaweicloud_vpc.test.id + subnet_id = huaweicloud_vpc_subnet.test.id + container_network_type = "overlay_l2" + service_network_cidr = "10.248.0.0/16" + cluster_version = "v1.15.11-r1" + delete_sfs = true +} + +resource "huaweicloud_compute_keypair" "test" { + name = "%s" + + lifecycle { + ignore_changes = [ + public_key, + ] + } +} + +resource "huaweicloud_cce_node" "test" { + name = "%s" + cluster_id = huaweicloud_cce_cluster.test.id + flavor_id = "s6.xlarge.2" + availability_zone = data.huaweicloud_availability_zones.test.names[0] + key_pair = huaweicloud_compute_keypair.test.name + max_pods = 30 + ecs_performance_type = "normal" + os = "CentOS 7.6" + iptype = "5_bgp" + bandwidth_charge_mode = "traffic" + sharetype = "PER" + bandwidth_size = 100 + + root_volume { + size = 40 + volumetype = "SAS" + } + data_volumes { + size = 100 + volumetype = "SAS" + } +} +`, rName, rName, rName, rName, rName) +} + +func testBCSInstanceV2_basic(rName, password string) string { + return fmt.Sprintf(` +%s + +resource "huaweicloud_bcs_instance" "test" { + depends_on = [huaweicloud_cce_node.test] + + name = "%s" + cluster_id = huaweicloud_cce_cluster.test.id + consensus = "etcdraft" + edition = 4 + eip_enable = true + enterprise_project_id = "%s" + fabric_version = "2.0" + password = "%s" + volume_type = "nfs" + org_disk_size = 100 + security_mechanism = "ECDSA" + orderer_node_num = 3 + delete_storage = true + + peer_orgs { + org_name = "organization01" + count = 1 + } + channels { + name = "channeldemo001" + org_names = [ + "organization01", + ] + } +} +`, testBCSInstanceV2_base(rName), rName, HW_ENTERPRISE_PROJECT_ID_TEST, password) +} + +func testBCSInstanceV2_kafka(rName, password string) string { + return fmt.Sprintf(` +%s + +resource "huaweicloud_bcs_instance" "test" { + depends_on = [huaweicloud_cce_node.test] + + name = "%s" + cluster_id = huaweicloud_cce_cluster.test.id + consensus = "kafka" + edition = 4 + eip_enable = true + enterprise_project_id = "%s" + fabric_version = "1.4" + password = "%s" + volume_type = "efs" + org_disk_size = 500 + database_type = "couchdb" + orderer_node_num = 2 + delete_storage = true + delete_obs = true + + couchdb { + user_name = "Administrator" + password = "%s" + } + peer_orgs { + org_name = "organization01" + count = 2 + } + peer_orgs { + org_name = "organization02" + count = 2 + } + channels { + name = "channeldemo001" + org_names = [ + "organization01", + "organization02", + ] + } + channels { + name = "channeldemo002" + org_names = [ + "organization02", + ] + } + sfs_turbo { + share_type = "STANDARD" + type = "efs-ha" + flavor = "sfs.turbo.standard" + availability_zone = data.huaweicloud_availability_zones.test.names[0] + } + kafka { + flavor = "c3.mini" + storage_size = 600 + availability_zone = [ + data.huaweicloud_availability_zones.test.names[0], + ] + } +} +`, testBCSInstanceV2_base(rName), rName, HW_ENTERPRISE_PROJECT_ID_TEST, password, password) +} diff --git a/vendor/github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains/requests.go b/vendor/github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains/requests.go new file mode 100644 index 0000000000..c5af559590 --- /dev/null +++ b/vendor/github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains/requests.go @@ -0,0 +1,239 @@ +package blockchains + +import ( + "github.com/huaweicloud/golangsdk" +) + +//CreateOpts is a struct which will be used to create a bcs instance +type CreateOpts struct { + Name string `json:"name" required:"true"` + ClusterType string `json:"cluster_type" required:"true"` + CreateNewCluster *bool `json:"create_new_cluster" required:"true"` + EnterpriseProjectId string `json:"enterprise_project_id" required:"true"` + FabricVersion string `json:"fabric_version" required:"true"` + Password string `json:"resource_password" required:"true"` + VersionType int `json:"version_type" required:"true"` + BlockChainType string `json:"blockchain_type,omitempty"` + Consensus string `json:"consensus,omitempty"` + SignAlgorithm string `json:"sign_algorithm,omitempty"` + VolumeType string `json:"volume_type,omitempty"` + EvsDiskType string `json:"evs_disk_type,omitempty"` + OrgDiskSize int `json:"org_disk_size,omitempty"` + DatabaseType string `json:"database_type,omitempty"` + OrdererNodeNumber int `json:"orderer_node_number,omitempty"` + EIPEnable bool `json:"use_eip,omitempty"` + BandwidthSize int `json:"bandwidth_size,omitempty"` + CCEClusterInfo *CCEClusterInfo `json:"cce_cluster_info,omitempty"` + CCECreateInfo *CCECreateInfo `json:"cce_create_info,omitempty"` + IEFDeployMode int `json:"ief_deploy_mode,omitempty"` + IEFNodesInfo []IEFNode `json:"ief_nodes_info,omitempty"` + PeerOrgs []PeerOrg `json:"peer_orgs,omitempty"` + Channels []ChannelInfo `json:"channels,omitempty"` + CouchDBInfo *CouchDBInfo `json:"couchdb_info,omitempty"` + SFSTurbo *SFSTurbo `json:"turbo_info,omitempty"` + Block *BlockInfo `json:"block_info,omitempty"` + Kafka *KafkaInfo `json:"kafka_create_info,omitempty"` + TC3Need bool `json:"tc3_need,omitempty"` + RestfulAPISupport bool `json:"restful_api_support,omitempty"` + IsInvitee bool `json:"is_invitee,omitempty"` + InvitorInfo *InvitorInfo `json:"invitor_infos,omitempty"` +} + +//CCEClusterInfo is the CCE cluster struct that will be used to associate when creating a bcs instance +type CCEClusterInfo struct { + ID string `json:"cluster_id" required:"true"` + Name string `json:"cluster_name" required:"true"` +} + +//CCECreateInfo is the struct that will be used to specify the creation of a new CCE cluster +//when creating a bcs instance +type CCECreateInfo struct { + NodeNum int `json:"node_num" required:"true"` + Flavor string `json:"node_flavor" required:"true"` + ClusterFlavor string `json:"cce_flavor" required:"true"` + Password string `json:"init_node_pwd" required:"true"` + AvailabilityZone string `json:"az" required:"true"` + PlatformType string `json:"cluster_platform_type" required:"true"` +} + +//IEFNode is the IEF node struct that will be used to associate when creating a bcs instance +type IEFNode struct { + ID string `json:"id" required:"true"` + Status string `json:"status" required:"true"` + IPAddress string `json:"public_ip_address" required:"true"` +} + +//PeerOrg is the peer organization struct that will be used to creating a bcs instance +type PeerOrg struct { + Name string `json:"name" required:"true"` + NodeCount int `json:"node_count" required:"true"` +} + +//ChannelInfo is the channel struct that will be used to creating a bcs instance +type ChannelInfo struct { + Name string `json:"name" required:"true"` + OrgNames []string `json:"org_names" required:"true"` + Description string `json:"desctiption,omitempty"` +} + +//CouchDBInfo is the couch database struct that will be used to creating a bcs instance +type CouchDBInfo struct { + UserName string `json:"user_name" required:"true"` + Password string `json:"password" required:"true"` +} + +//SFSTurbo is the turbo struct that will be used to creating a bcs instance +type SFSTurbo struct { + ShareType string `json:"share_type" required:"true"` + Type string `json:"type" required:"true"` + AvailabilityZone string `json:"availability_zone" required:"true"` + Flavor string `json:"resource_spec_code" required:"true"` +} + +//BlockInfo is the turbo struct that will be used to creating a bcs instance +type BlockInfo struct { + BatchTimeout int `json:"batch_timeout,omitempty"` + MaxMessageCount int `json:"max_message_count,omitempty"` + PreferredMaxbytes int `json:"preferred_maxbytes,omitempty"` +} + +//KafkaInfo is the block generation struct that be used to config when creating a bcs instance +type KafkaInfo struct { + Flavor string `json:"spec" required:"true"` + Storage int `json:"storage" required:"true"` + AvailabilityZone string `json:"az" required:"true"` +} + +//InvitorInfo is the invitor struct that be used to config when creating a bcs instance +type InvitorInfo struct { + TenantID string `json:"tenant_id" required:"true"` + ProjectID string `json:"project_id" required:"true"` + BlockchainID string `json:"blockchain_id" required:"true"` +} + +type CreateOptsBuilder interface { + ToInstancesCreateMap() (map[string]interface{}, error) +} + +func (opts CreateOpts) ToInstancesCreateMap() (map[string]interface{}, error) { + b, err := golangsdk.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + return b, nil +} + +//Create is a method by which can be able to access the create function that create a bcs instance +func Create(client *golangsdk.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToInstancesCreateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(rootURL(client), b, &r.Body, &golangsdk.RequestOpts{ + OkCodes: []int{200, 202}, + }) + return +} + +//DeleteOpts is a struct which will be used to delete an existing bcs instance +type DeleteOpts struct { + IsDeleteStorage bool `q:"is_delete_storage"` + IsDeleteOBS bool `q:"is_delete_obs"` + IsDeleteResource bool `q:"is_delete_resource"` +} + +type DeleteOptsBuilder interface { + ToInstanceDeleteQuery() (string, error) +} + +func (opts DeleteOpts) ToInstanceDeleteQuery() (string, error) { + q, err := golangsdk.BuildQueryString(opts) + if err != nil { + return "", err + } + return q.String(), err +} + +//Delete is a method to delete an existing bcs instance +func Delete(client *golangsdk.ServiceClient, opts DeleteOptsBuilder, id string) (r DeleteResult) { + url := resourceURL(client, id) + if opts != nil { + query, err := opts.ToInstanceDeleteQuery() + if err != nil { + r.Err = err + return + } + url += query + } + _, r.Err = client.Delete(url, &golangsdk.RequestOpts{ + OkCodes: []int{200, 202, 204}, + JSONResponse: nil, + MoreHeaders: map[string]string{"Content-Type": "application/json"}, + }) + return +} + +//Get is a method to obtain the detailed information of an existing bcs instance +func Get(client *golangsdk.ServiceClient, id string) (r ShowResult) { + _, r.Err = client.Get(resourceURL(client, id), &r.Body, nil) + return +} + +//GetStatus is a method to obtain all block status of an existing bcs instance +func GetStatus(client *golangsdk.ServiceClient, id string) (r StatusResult) { + _, r.Err = client.Get(extraURL(client, id, "status"), &r.Body, nil) + return +} + +//List is a method to obtain the detailed information list of all existing bcs instance +func List(client *golangsdk.ServiceClient) (r ListResult) { + _, r.Err = client.Get(rootURL(client), &r.Body, nil) + return +} + +//GetNodes is a method to obtain the node information list of an existing bcs instance +func GetNodes(client *golangsdk.ServiceClient, id string) (r NodesResult) { + _, r.Err = client.Get(extraURL(client, id, "nodes"), &r.Body, nil) + return +} + +//UpdateOpts is a struct which will be used to update an existing bcs instance +type UpdateOpts struct { + NodePeer []NodePeer `json:"node_orgs" required:"true"` + PublicIPs []IEFNode `json:"publicips,omitempty"` +} + +//NodePeer is the peer organization struct that will be used to add a peer organization to an existing bcs instance +type NodePeer struct { + Name string `json:"name" required:"true"` + Count int `json:"node_count" required:"true"` + PVCName string `json:"pvc_name,omitempty"` +} + +type UpdateOptsBuilder interface { + ToInstancesUpdateMap() (map[string]interface{}, error) +} + +func (opts UpdateOpts) ToInstancesUpdateMap() (map[string]interface{}, error) { + b, err := golangsdk.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + return b, nil +} + +//Update is a method to update an existing bcs instance +func Update(client *golangsdk.ServiceClient, opts UpdateOptsBuilder, id string) (r UpdateResult) { + b, err := opts.ToInstancesUpdateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(resourceURL(client, id), b, nil, &golangsdk.RequestOpts{ + OkCodes: []int{200, 202}, + }) + return +} diff --git a/vendor/github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains/results.go b/vendor/github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains/results.go new file mode 100644 index 0000000000..71a129161c --- /dev/null +++ b/vendor/github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains/results.go @@ -0,0 +1,266 @@ +package blockchains + +import "github.com/huaweicloud/golangsdk" + +type commonResult struct { + golangsdk.Result +} + +//CreateResult is a struct that represents the result of CreateNewBlockchain +type CreateResult struct { + commonResult +} + +type CreateResponse struct { + ID string `json:"blockchain_id"` + Name string `json:"blockchain_name"` +} + +func (r CreateResult) Extract() (*CreateResponse, error) { + var res CreateResponse + err := r.ExtractInto(&res) + return &res, err +} + +//DeleteResult is a struct that represents the result of DeleteBlockchain +type DeleteResult struct { + commonResult +} + +func (r DeleteResult) Extract() error { + return r.Err +} + +//ShowResult is a struct that represents the result of ShowBlockchainDetail +type ShowResult struct { + commonResult +} + +type BCSInstance struct { + Basic Basic `json:"basic_info"` + Channels []Channel `json:"channels"` + Peer []Peer `json:"peer_info"` + LightPeer []Peer `json:"loght_peer_info"` + Orderer Peer `json:"orderer_info"` + CouchDB CouchDB `json:"couch_db_info"` + DMSKafka DMSKafka `json:"dms_kafka_info"` + IEF IEF `json:"ief_info"` + SFS SFS `json:"sfs_info"` + Agent Peer `json:"agent_info"` + RestfulAPI Peer `json:"restapi_info"` + PVC PVC `json:"evs_pvc_info"` + TaskServer Peer `json:"tc3_taskserver_info"` + OBS OBS `json:"obs_bucket_info"` +} + +type Basic struct { + ID string `json:"id"` + Name string `json:"name"` + KernelType string `json:"kernel_type"` + Version string `json:"version"` + VersionType int `json:"version_type"` + VolumeType string `json:"volume_type"` + ServiceType string `json:"service_type"` + PurchaseType string `json:"purchase_type"` + SignAlgorithm string `json:"sign_algorithm"` + Consensus string `json:"consensus"` + ChargingMode int `json:"charging_mode"` + DatabaseType string `json:"database_type"` + ClusterID string `json:"cluster_id"` + ClusterName string `json:"cluster_name"` + ClusterType string `json:"cluster_type"` + ClusterPlatformType string `json:"cluster_platform_type"` + ClusterAvailabilityZone string `json:"cluster_az"` + CreatedTime string `json:"created_time"` + DeployType string `json:"deploy_type"` + DeployScale int `json:"deploy_scale"` + DeployStatus int `json:"deploy_status"` + DetailStatus DetailStatus `json:"detail_status"` + IsCrossRegion bool `json:"is_cross_region"` + IsSupportRollback bool `json:"is_support_rollback"` + IsSupportRestful bool `json:"is_support_restful"` + IsSupportTc3 bool `json:"is_support_tc3"` + IsOldService bool `json:"is_old_service"` + OldServiceVersion string `json:"old_service_version"` + AgentPortalAddress []string `json:"agent_portal_addrs"` + Status string `json:"status"` + ProcessStatus string `json:"process_status"` + Tc3TaskServerPortalAddrs []string `json:"tc3_taskserver_portal_addrs"` + TotalDeployPeer int `json:"total_deploy_peer"` + OrderStatus int `json:"order_status"` + OrderInfo OrderInfo `json:"order_info"` + OrderFadeCache int `json:"order_fade_cache"` + OrderFadeEnable bool `json:"order_fade_enable"` + IEFClusterInfo IEFCluster `json:"ief_cluster_info"` + IEFAPIVersion string `json:"iefapi_version"` +} + +type Channel struct { + Name string `json:"name"` + OrgNames []string `json:"org_names"` + OrgNameHash []string `json:"org_name_hash"` + Peers map[string][]string `json:"peers"` +} + +type Peer struct { + Name string `json:"name"` + NodeCount int `json:"node_cnt"` + Status string `json:"status"` + StatusDetail string `json:"status_detail"` + PVCName string `json:"pvc_name"` + Address []PeerAddress `json:"address"` +} + +type PeerAddress struct { + DomainPort string `json:"domain_port"` + IPPort string `json:"ip_port"` +} + +type CouchDB struct { + User string `json:"user"` +} + +type DMSKafka struct { + Address []string `json:"addr"` + Name string `json:"name"` + Status string `json:"status"` + NodeCount int `json:"node_cnt"` + OrderFadeEnable bool `json:"order_fade_enable"` + OrderFadeCache int `json:"order_fade_cache"` +} + +type IEF struct { + DeployMode int `json:"deploy_mode"` +} + +type IEFCluster struct { + GroupID string `json:"group_id"` + GroupName string `json:"group_name"` + InstanceID string `json:"instance_id"` + InstanceName string `json:"instance_name"` +} + +type SFS struct { + Name string `json:"name"` + PVCName string `json:"pvc_name"` + Address string `json:"addr"` + Type string `json:"type"` +} + +type PVC struct { + DeployMode int `json:"deploy_mode"` +} + +type OBS struct { + Name string `json:"name"` + Address string `json:"addr"` +} + +type DetailStatus struct { + AgentStatus string `json:"agent_status"` + ConsensusStatus string `json:"consensus_status"` + OrgStatus string `json:"org_status"` + PeerStatus string `json:"peer_status"` + PluginStatus string `json:"plugin_status"` +} + +type OrderInfo struct { + Delete int `json:"delete"` + Operate int `json:"operate"` + OrderID string `json:"order_id"` + OrderStatus int `json:"order_status"` + OrderType int `json:"order_type"` + Release int `json:"release"` + ResourceErrorCode string `json:"resource_error_code"` + ResourceStatus int `json:"resource_status"` +} + +func (r ShowResult) Extract() (*BCSInstance, error) { + var res BCSInstance + err := r.ExtractInto(&res) + return &res, err +} + +//ListResult is a struct that represents the result of ListBlockchain +type ListResult struct { + commonResult +} + +type BlockChain struct { + ID string `json:"id"` + Name string `json:"name"` +} + +func (r ListResult) Extract() (*[]BlockChain, error) { + var s struct { + BlockChains []BlockChain `json:"blockchains"` + } + err := r.ExtractInto(&s) + return &s.BlockChains, err +} + +//StatusResult is a struct that represents the result of ShowBlockchainStatus +type StatusResult struct { + commonResult +} + +type Status struct { + BCSStatus StatusDetail `json:"bcs"` + EIPStatus StatusDetail `json:"eip"` + SFSStatus StatusDetail `json:"sfs"` + OBSStatus StatusDetail `json:"obs"` + KafkaStatus StatusDetail `json:"kafka"` + CCEStatus CCEEngine `json:"cce"` +} + +type CCEEngine struct { + Cluster StatusDetail `json:"cluster"` + Network StatusDetail `json:"network"` + SecurityGroup StatusDetail `json:"security_group"` +} + +type StatusDetail struct { + StartTime string `json:"start_time"` + EndTime string `json:"end_time"` + Status string `json:"status"` + Detail string `json:"detail"` +} + +func (r StatusResult) Extract() (*Status, error) { + var s Status + err := r.ExtractInto(&s) + return &s, err +} + +//NodesResult is a struct that represents the result of ShowBlockchainNode +type NodesResult struct { + commonResult +} + +type Org struct { + OrgMSPID string `json:"org_msp_id"` + OrgDomain string `json:"org_domain"` + Peers map[string]Node `json:"peers"` +} + +type Node struct { + Port string `json:"ip_port"` + Channels []string `json:"channels"` +} + +func (r NodesResult) Extract() (*map[string]Org, error) { + var s struct { + NodeOrgs map[string]Org `json:"node_orgs"` + } + err := r.ExtractInto(&s) + return &s.NodeOrgs, err +} + +//UpdateResult is a struct which represents the result of UpdateBlockchain +type UpdateResult struct { + commonResult +} + +func (r UpdateResult) Extract() error { + return r.Err +} diff --git a/vendor/github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains/urls.go b/vendor/github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains/urls.go new file mode 100644 index 0000000000..4cac1429f1 --- /dev/null +++ b/vendor/github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains/urls.go @@ -0,0 +1,17 @@ +package blockchains + +import "github.com/huaweicloud/golangsdk" + +const resourcePath = "blockchains" + +func rootURL(c *golangsdk.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func resourceURL(c *golangsdk.ServiceClient, instanceID string) string { + return c.ServiceURL(resourcePath, instanceID) +} + +func extraURL(c *golangsdk.ServiceClient, instanceID, extra string) string { + return c.ServiceURL(resourcePath, instanceID, extra) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 767aafd910..e458e4991b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -271,6 +271,7 @@ github.com/huaweicloud/golangsdk/openstack/autoscaling/v1/groups github.com/huaweicloud/golangsdk/openstack/autoscaling/v1/instances github.com/huaweicloud/golangsdk/openstack/autoscaling/v1/policies github.com/huaweicloud/golangsdk/openstack/autoscaling/v1/tags +github.com/huaweicloud/golangsdk/openstack/bcs/v2/blockchains github.com/huaweicloud/golangsdk/openstack/blockstorage/extensions/volumeactions github.com/huaweicloud/golangsdk/openstack/blockstorage/v2/volumes github.com/huaweicloud/golangsdk/openstack/bss/v2/orders From 934416d180242631e54fa8b3943412f1c9bb9f3b Mon Sep 17 00:00:00 2001 From: Lance52259 Date: Wed, 21 Apr 2021 20:10:07 +0800 Subject: [PATCH 2/2] bcs instance resource modification --- docs/resources/bcs_instance.md | 24 ++++++------------- .../resource_huaweicloud_bcs_instance.go | 11 +++++---- .../resource_huaweicloud_bcs_instance_test.go | 8 +++---- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/docs/resources/bcs_instance.md b/docs/resources/bcs_instance.md index b07f0b20f7..a6778774c5 100644 --- a/docs/resources/bcs_instance.md +++ b/docs/resources/bcs_instance.md @@ -23,10 +23,9 @@ data "huaweicloud_cce_cluster" "test" { resource "huaweicloud_bcs_instance" "test" { name = var.instance_name - cluster_id = data.huaweicloud_cce_cluster.test.id + cce_cluster_id = data.huaweicloud_cce_cluster.test.id consensus = "etcdraft" edition = 1 - eip_enable = true enterprise_project_id = var.enterprise_project_id fabric_version = "2.0" password = var.instance_password @@ -71,10 +70,9 @@ data "huaweicloud_cce_cluster" "test" { resource "huaweicloud_bcs_instance" "test" { name = var.instance_name blockchain_type = "private" - cluster_id = data.huaweicloud_cce_cluster.test.id + cce_cluster_id = data.huaweicloud_cce_cluster.test.id consensus = "kafka" edition = 4 - eip_enable = true fabric_version = "1.4" enterprise_project_id = var.enterprise_project_id password = var.instance_password @@ -144,7 +142,6 @@ The following arguments are supported: * `edition` - (Required, Int, ForceNew) Specifies Service edition of the BCS instance. Valid values are `1`, `2` and `4`. - For the specifications corresponding to these three versions, please refer to the [specification table](#jump). Changing this will create a new instance. * `fabric_version` - (Required, String, ForceNew) Specifies version of fabric for the BCS instance. @@ -157,10 +154,9 @@ The following arguments are supported: Changing this will create a new instance. * `orderer_node_num` - (Required, Int, ForceNew) Specifies the number of peers in the orderer organizaion. - For the number of orderer nodes, please refer to the [specification table](#jump). Changing this will create a new instance. -* `cluster_id` - (Required, String, ForceNew) Specifies the ID of the CCE cluster to attach to the BCS instance. +* `cce_cluster_id` - (Required, String, ForceNew) Specifies the ID of the CCE cluster to attach to the BCS instance. The BCS service needs to exclusively occupy the CCE cluster. Please make sure that the CCE cluster is not occupied before deploying the BCS service. Changing this will create a new instance. @@ -185,14 +181,16 @@ The following arguments are supported: The specifications are as follows when `volume_type` is `nfs`: * The minimum storage capacity of basic edition is 40 GB. * The minimum storage capacity of enterprise and professional edition is 100 GB. - * The minimum storage capacity of platinum edition is 500 GB. + +* `block_info` - (Optional, List, ForceNew) Specifies the configuration of block generation. + The block_info object structure is documented below. * `blockchain_type` - (Optional, String, ForceNew) Specifies the blockchain type of the BCS instance. Valid values are `private` and `union`. Default is `private`. Changing this will create a new instance. * `channels` - (Optional, List, ForceNew) Specifies an array of one or more channels to attach to the BCS - instance. For the maximum number of channels, please refer to the specification table. + instance. If omitted, the bcs instance will create a `channels` named `channel` by default. Changing this will create a new instance. The channels object structure is documented below. @@ -246,7 +244,6 @@ The `peer_orgs` block supports: Changing this creates a new instance. * `count` - (Required, Int, ForceNew) Specifies the number of peers in organization. - For the number of organization nodes, please refer to the [specification table](#jump). Changing this creates a new instance. The `channels` block supports: @@ -318,13 +315,6 @@ The `kafka` block supports: * The minimum storage capacity of middle type is 2400GB. * The minimum storage capacity of high type is 4800GB. -Specification Table of BCS instance - | Edition | Version Type | Organizations(Max) | Peers(Max) | Orderers(Max) | Channels(Max) | - | ------- | ------------ | ------------------ | ---------- | ------------- | ------------- | - | Basic | 1 | 1 | 2 | 1 | 1 | - | Professional | 4 | 2 | 2 | 3 | 2 | - | Enterprise | 2 | 5 | 2 | 4 | 4 | - ## Attributes Reference In addition to all arguments above, the following attributes are exported: diff --git a/huaweicloud/resource_huaweicloud_bcs_instance.go b/huaweicloud/resource_huaweicloud_bcs_instance.go index 2fc0abc6ef..970e41f7c7 100644 --- a/huaweicloud/resource_huaweicloud_bcs_instance.go +++ b/huaweicloud/resource_huaweicloud_bcs_instance.go @@ -103,9 +103,10 @@ func resourceBCSInstanceV2() *schema.Resource { "eip_enable": { Type: schema.TypeBool, Optional: true, + Default: true, ForceNew: true, }, - "cluster_id": { + "cce_cluster_id": { Type: schema.TypeString, Required: true, ForceNew: true, @@ -346,14 +347,14 @@ func resourceBCSInstanceV2() *schema.Resource { } func resourceBCSInstanceV2Create(d *schema.ResourceData, meta interface{}) error { - var FALSE bool = false + var newCluster bool = false config := meta.(*config.Config) client, err := config.BcsV2Client(GetRegion(d, config)) if err != nil { return fmt.Errorf("Error creating HuaweiCloud Blockchain client: %s", err) } createOpts := blockchains.CreateOpts{ - CreateNewCluster: &FALSE, + CreateNewCluster: &newCluster, Name: d.Get("name").(string), VersionType: d.Get("edition").(int), FabricVersion: d.Get("fabric_version").(string), @@ -377,7 +378,7 @@ func resourceBCSInstanceV2Create(d *schema.ResourceData, meta interface{}) error Kafka: resourceKafkaCreateInfo(d), } - if v, err := resourceClusterInfo(config, d.Get("cluster_id").(string), GetRegion(d, config)); err != nil { + if v, err := resourceClusterInfo(config, d.Get("cce_cluster_id").(string), GetRegion(d, config)); err != nil { return fmt.Errorf("Get cluster information failed: %s ", err) } else { createOpts.ClusterType = "cce" @@ -431,7 +432,7 @@ func resourceBCSInstanceV2Read(d *schema.ResourceData, meta interface{}) error { d.Set("status", instance.Basic.Status) d.Set("tc3_need", instance.CouchDB != (blockchains.CouchDB{})) d.Set("cluster_type", instance.Basic.ClusterType) - d.Set("cluster_id", instance.Basic.ClusterID) + d.Set("cce_cluster_id", instance.Basic.ClusterID) d.Set("version", instance.Basic.Version) d.Set("purchase_type", instance.Basic.PurchaseType) d.Set("cross_region_support", instance.Basic.IsCrossRegion) diff --git a/huaweicloud/resource_huaweicloud_bcs_instance_test.go b/huaweicloud/resource_huaweicloud_bcs_instance_test.go index 0c8a013245..2abcd1ee71 100644 --- a/huaweicloud/resource_huaweicloud_bcs_instance_test.go +++ b/huaweicloud/resource_huaweicloud_bcs_instance_test.go @@ -202,7 +202,7 @@ resource "huaweicloud_cce_node" "test" { iptype = "5_bgp" bandwidth_charge_mode = "traffic" sharetype = "PER" - bandwidth_size = 100 + bandwidth_size = 5 root_volume { size = 40 @@ -224,10 +224,9 @@ resource "huaweicloud_bcs_instance" "test" { depends_on = [huaweicloud_cce_node.test] name = "%s" - cluster_id = huaweicloud_cce_cluster.test.id + cce_cluster_id = huaweicloud_cce_cluster.test.id consensus = "etcdraft" edition = 4 - eip_enable = true enterprise_project_id = "%s" fabric_version = "2.0" password = "%s" @@ -259,10 +258,9 @@ resource "huaweicloud_bcs_instance" "test" { depends_on = [huaweicloud_cce_node.test] name = "%s" - cluster_id = huaweicloud_cce_cluster.test.id + cce_cluster_id = huaweicloud_cce_cluster.test.id consensus = "kafka" edition = 4 - eip_enable = true enterprise_project_id = "%s" fabric_version = "1.4" password = "%s"