diff --git a/huaweicloud/config.go b/huaweicloud/config.go index 342b2b61f2..beb3194d66 100644 --- a/huaweicloud/config.go +++ b/huaweicloud/config.go @@ -500,3 +500,13 @@ func (c *Config) dcsV1Client(region string) (*golangsdk.ServiceClient, error) { Availability: c.getHwEndpointType(), }) } + +func (c *Config) sdkClient(region, serviceType string) (*golangsdk.ServiceClient, error) { + return huaweisdk.NewSDKClient( + c.HwClient, + golangsdk.EndpointOpts{ + Region: c.determineRegion(region), + Availability: c.getHwEndpointType(), + }, + serviceType) +} diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index 4b65621afc..3ccbdef971 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -296,6 +296,7 @@ func Provider() terraform.ResourceProvider { "huaweicloud_vbs_backup_v2": resourceVBSBackupV2(), "huaweicloud_cts_tracker_v1": resourceCTSTrackerV1(), "huaweicloud_maas_task_v1": resourceMaasTaskV1(), + "huaweicloud_dws_cluster": resourceDwsCluster(), }, ConfigureFunc: configureProvider, diff --git a/huaweicloud/resource_huaweicloud_dws_cluster.go b/huaweicloud/resource_huaweicloud_dws_cluster.go new file mode 100644 index 0000000000..a49c965723 --- /dev/null +++ b/huaweicloud/resource_huaweicloud_dws_cluster.go @@ -0,0 +1,662 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file at +// https://www.github.com/huaweicloud/magic-modules +// +// ---------------------------------------------------------------------------- + +package huaweicloud + +import ( + "fmt" + "log" + "reflect" + "time" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/huaweicloud/golangsdk" +) + +func resourceDwsCluster() *schema.Resource { + return &schema.Resource{ + Create: resourceDwsClusterCreate, + Read: resourceDwsClusterRead, + Delete: resourceDwsClusterDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "network_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "node_type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "number_of_node": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + + "security_group_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "user_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "user_pwd": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "vpc_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "availability_zone": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + }, + + "port": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + Optional: true, + ForceNew: true, + }, + + "public_ip": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "eip_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + }, + "public_bind_type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + }, + }, + }, + }, + + "created": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "endpoints": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "connect_info": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "jdbc_url": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + + "id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "public_endpoints": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "jdbc_url": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "public_connect_info": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + + "recent_event": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + }, + + "status": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "sub_status": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "task_status": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "updated": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "version": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceDwsClusterCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + client, err := config.sdkClient(GetRegion(d, config), "dws") + if err != nil { + return fmt.Errorf("Error creating sdk client, err=%s", err) + } + + opts := make(map[string]interface{}) + userPwdProp := d.Get("user_pwd") + e, err := isEmptyValue(reflect.ValueOf(userPwdProp)) + if err != nil { + return err + } + if !e { + opts["user_pwd"] = userPwdProp + } + + availabilityZoneProp := d.Get("availability_zone") + e, err = isEmptyValue(reflect.ValueOf(availabilityZoneProp)) + if err != nil { + return err + } + if !e { + opts["availability_zone"] = availabilityZoneProp + } + + nameProp := d.Get("name") + e, err = isEmptyValue(reflect.ValueOf(nameProp)) + if err != nil { + return err + } + if !e { + opts["name"] = nameProp + } + + networkIDProp := d.Get("network_id") + e, err = isEmptyValue(reflect.ValueOf(networkIDProp)) + if err != nil { + return err + } + if !e { + opts["subnet_id"] = networkIDProp + } + + nodeTypeProp := d.Get("node_type") + e, err = isEmptyValue(reflect.ValueOf(nodeTypeProp)) + if err != nil { + return err + } + if !e { + opts["node_type"] = nodeTypeProp + } + + numberOFNodeProp := d.Get("number_of_node") + e, err = isEmptyValue(reflect.ValueOf(numberOFNodeProp)) + if err != nil { + return err + } + if !e { + opts["number_of_node"] = numberOFNodeProp + } + + portProp := d.Get("port") + e, err = isEmptyValue(reflect.ValueOf(portProp)) + if err != nil { + return err + } + if !e { + opts["port"] = portProp + } + + publicIPProp, err := expandDwsClusterPublicIP(d.Get("public_ip")) + if err != nil { + return err + } + e, err = isEmptyValue(reflect.ValueOf(publicIPProp)) + if err != nil { + return err + } + if !e { + opts["public_ip"] = publicIPProp + } + + securityGroupIDProp := d.Get("security_group_id") + e, err = isEmptyValue(reflect.ValueOf(securityGroupIDProp)) + if err != nil { + return err + } + if !e { + opts["security_group_id"] = securityGroupIDProp + } + + userNameProp := d.Get("user_name") + e, err = isEmptyValue(reflect.ValueOf(userNameProp)) + if err != nil { + return err + } + if !e { + opts["user_name"] = userNameProp + } + + vpcIDProp := d.Get("vpc_id") + e, err = isEmptyValue(reflect.ValueOf(vpcIDProp)) + if err != nil { + return err + } + if !e { + opts["vpc_id"] = vpcIDProp + } + + url, err := replaceVars(d, "clusters", nil) + if err != nil { + return err + } + url = client.ServiceURL(url) + + log.Printf("[DEBUG] Creating new Cluster: %#v", opts) + r := golangsdk.Result{} + _, r.Err = client.Post( + url, + &map[string]interface{}{"cluster": opts}, + &r.Body, + &golangsdk.RequestOpts{OkCodes: successHTTPCodes}) + if r.Err != nil { + return fmt.Errorf("Error creating Cluster: %s", r.Err) + } + + pathParameters := map[string][]string{ + "id": []string{"cluster", "id"}, + } + var data = make(map[string]string) + for key, path := range pathParameters { + value, err := navigateMap(r.Body, path) + if err != nil { + return fmt.Errorf("Error retrieving async operation path parameter: %s", err) + } + data[key] = value.(string) + } + url, err = replaceVars(d, "clusters/{id}", data) + if err != nil { + return err + } + url = client.ServiceURL(url) + + obj, err := waitToFinish( + []string{"AVAILABLE"}, + []string{"CREATING", "Pending"}, + d.Timeout(schema.TimeoutCreate), + 1*time.Second, + func() (interface{}, string, error) { + r := golangsdk.Result{} + _, r.Err = client.Get( + url, &r.Body, + &golangsdk.RequestOpts{MoreHeaders: map[string]string{"Content-Type": "application/json"}}) + if r.Err != nil { + return nil, "", nil + } + + code, err := navigateMap(r.Body, []string{"failed_reasons", "error_code"}) + if err == nil { + if v, err := convertToInt(code); err == nil { + msg, err := navigateMap(r.Body, []string{"failed_reasons", "error_msg"}) + if err != nil { + return nil, "", fmt.Errorf("async operation failed: %v", msg) + } + return nil, "", fmt.Errorf("async operation failed: error code = %v", v) + } + } + + status, err := navigateMap(r.Body, []string{"cluster", "status"}) + if err != nil { + return nil, "", nil + } + return r.Body, status.(string), nil + }, + ) + + if err != nil { + return err + } + id, err := navigateMap(obj, []string{"cluster", "id"}) + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id.(string)) + + return resourceDwsClusterRead(d, meta) +} + +func resourceDwsClusterRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + client, err := config.sdkClient(GetRegion(d, config), "dws") + if err != nil { + return fmt.Errorf("Error creating sdk client, err=%s", err) + } + + url, err := replaceVars(d, "clusters/{id}", nil) + if err != nil { + return err + } + url = client.ServiceURL(url) + + r := golangsdk.Result{} + _, r.Err = client.Get( + url, &r.Body, + &golangsdk.RequestOpts{MoreHeaders: map[string]string{"Content-Type": "application/json"}}) + if r.Err != nil { + return fmt.Errorf("Error reading %s: %s", fmt.Sprintf("DwsCluster %q", d.Id()), r.Err) + } + v, err := navigateMap(r.Body, []string{"cluster"}) + if err != nil { + return fmt.Errorf("Error reading %s: the result does not contain cluster", fmt.Sprintf("DwsCluster %q", d.Id())) + } + res := v.(map[string]interface{}) + + if v, ok := res["availability_zone"]; ok { + if err := d.Set("availability_zone", v); err != nil { + return fmt.Errorf("Error reading Cluster:availability_zone, err: %s", err) + } + } + + if v, ok := res["created"]; ok { + if err := d.Set("created", v); err != nil { + return fmt.Errorf("Error reading Cluster:created, err: %s", err) + } + } + + if v, ok := res["endpoints"]; ok { + endpointsProp, err := flattenDwsClusterEndpoints(v) + if err != nil { + return fmt.Errorf("Error reading Cluster:endpoints, err: %s", err) + } + if err := d.Set("endpoints", endpointsProp); err != nil { + return fmt.Errorf("Error reading Cluster:endpoints, err: %s", err) + } + } + + if v, ok := res["id"]; ok { + if err := d.Set("id", v); err != nil { + return fmt.Errorf("Error reading Cluster:id, err: %s", err) + } + } + + if v, ok := res["port"]; ok { + if err := d.Set("port", v); err != nil { + return fmt.Errorf("Error reading Cluster:port, err: %s", err) + } + } + + if v, ok := res["public_endpoints"]; ok { + publicEndpointsProp, err := flattenDwsClusterPublicEndpoints(v) + if err != nil { + return fmt.Errorf("Error reading Cluster:public_endpoints, err: %s", err) + } + if err := d.Set("public_endpoints", publicEndpointsProp); err != nil { + return fmt.Errorf("Error reading Cluster:public_endpoints, err: %s", err) + } + } + + if v, ok := res["public_ip"]; ok { + publicIPProp, err := flattenDwsClusterPublicIP(v) + if err != nil { + return fmt.Errorf("Error reading Cluster:public_ip, err: %s", err) + } + if err := d.Set("public_ip", publicIPProp); err != nil { + return fmt.Errorf("Error reading Cluster:public_ip, err: %s", err) + } + } + + if v, ok := res["recent_event"]; ok { + if err := d.Set("recent_event", v); err != nil { + return fmt.Errorf("Error reading Cluster:recent_event, err: %s", err) + } + } + + if v, ok := res["status"]; ok { + if err := d.Set("status", v); err != nil { + return fmt.Errorf("Error reading Cluster:status, err: %s", err) + } + } + + if v, ok := res["sub_status"]; ok { + if err := d.Set("sub_status", v); err != nil { + return fmt.Errorf("Error reading Cluster:sub_status, err: %s", err) + } + } + + if v, ok := res["task_status"]; ok { + if err := d.Set("task_status", v); err != nil { + return fmt.Errorf("Error reading Cluster:task_status, err: %s", err) + } + } + + if v, ok := res["updated"]; ok { + if err := d.Set("updated", v); err != nil { + return fmt.Errorf("Error reading Cluster:updated, err: %s", err) + } + } + + if v, ok := res["version"]; ok { + if err := d.Set("version", v); err != nil { + return fmt.Errorf("Error reading Cluster:version, err: %s", err) + } + } + + return nil +} + +func resourceDwsClusterDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + client, err := config.sdkClient(GetRegion(d, config), "dws") + if err != nil { + return fmt.Errorf("Error creating sdk client, err=%s", err) + } + + url, err := replaceVars(d, "clusters/{id}", nil) + if err != nil { + return err + } + url = client.ServiceURL(url) + + log.Printf("[DEBUG] Deleting Cluster %q", d.Id()) + r := golangsdk.Result{} + _, r.Err = client.Delete(url, &golangsdk.RequestOpts{ + OkCodes: successHTTPCodes, + JSONResponse: &r.Body, + MoreHeaders: map[string]string{"Content-Type": "application/json"}, + JSONBody: map[string]interface{}{ + "keep_last_manual_snapshot": 0, + }, + }) + if r.Err != nil { + return fmt.Errorf("Error deleting Cluster %q: %s", d.Id(), r.Err) + } + + _, err = waitToFinish( + []string{"Done"}, []string{"Pending"}, + d.Timeout(schema.TimeoutDelete), + 1*time.Second, + func() (interface{}, string, error) { + resp, err := client.Get( + url, &r.Body, + &golangsdk.RequestOpts{MoreHeaders: map[string]string{"Content-Type": "application/json"}}) + + if err != nil { + if _, ok := err.(golangsdk.ErrDefault404); ok { + return resp, "Done", nil + } + return nil, "", nil + } + return resp, "Pending", nil + }, + ) + return err +} + +func flattenDwsClusterEndpoints(v interface{}) (interface{}, error) { + if v == nil { + return nil, nil + } + l := v.([]interface{}) + result := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + if val, ok := original["connect_info"]; ok { + transformed["connect_info"] = val + } + + if val, ok := original["jdbc_url"]; ok { + transformed["jdbc_url"] = val + } + + result = append(result, transformed) + } + + return result, nil +} + +func flattenDwsClusterPublicEndpoints(v interface{}) (interface{}, error) { + if v == nil { + return nil, nil + } + l := v.([]interface{}) + result := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + if val, ok := original["jdbc_url"]; ok { + transformed["jdbc_url"] = val + } + + if val, ok := original["public_connect_info"]; ok { + transformed["public_connect_info"] = val + } + + result = append(result, transformed) + } + + return result, nil +} + +func flattenDwsClusterPublicIP(v interface{}) (interface{}, error) { + if v == nil { + return nil, nil + } + original := v.(map[string]interface{}) + transformed := make(map[string]interface{}) + + if val, ok := original["eip_id"]; ok { + transformed["eip_id"] = val + } + + if val, ok := original["public_bind_type"]; ok { + transformed["public_bind_type"] = val + } + + return []interface{}{transformed}, nil +} + +func expandDwsClusterPublicIP(v interface{}) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + eipIDProp := original["eip_id"] + e, err := isEmptyValue(reflect.ValueOf(eipIDProp)) + if err != nil { + return nil, err + } + if !e { + transformed["eip_id"] = eipIDProp + } + + publicBindTypeProp := original["public_bind_type"] + e, err = isEmptyValue(reflect.ValueOf(publicBindTypeProp)) + if err != nil { + return nil, err + } + if !e { + transformed["public_bind_type"] = publicBindTypeProp + } + + return transformed, nil +} diff --git a/huaweicloud/resource_huaweicloud_dws_cluster_test.go b/huaweicloud/resource_huaweicloud_dws_cluster_test.go new file mode 100644 index 0000000000..a64c66dbf3 --- /dev/null +++ b/huaweicloud/resource_huaweicloud_dws_cluster_test.go @@ -0,0 +1,128 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file at +// https://www.github.com/huaweicloud/magic-modules +// +// ---------------------------------------------------------------------------- + +package huaweicloud + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/huaweicloud/golangsdk" +) + +func TestAccDwsCluster_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDwsClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDwsCluster_basic(acctest.RandString(10)), + Check: resource.ComposeTestCheckFunc( + testAccCheckDwsClusterExists(), + ), + }, + }, + }) +} + +func testAccDwsCluster_basic(val string) string { + return fmt.Sprintf(` +resource "huaweicloud_networking_secgroup_v2" "secgroup" { + name = "security_group_2%s" + description = "terraform security group" +} + +resource "huaweicloud_dws_cluster" "cluster" { + node_type = "dws.m3.xlarge" + number_of_node = 3 + network_id = "%s" + vpc_id = "%s" + security_group_id = "${huaweicloud_networking_secgroup_v2.secgroup.id}" + availability_zone = "%s" + name = "terraform_dws_cluster_test%s" + user_name = "test_cluster_admin" + user_pwd = "cluster123@!" + + timeouts { + create = "30m" + delete = "30m" + } +} + `, val, OS_NETWORK_ID, OS_VPC_ID, OS_AVAILABILITY_ZONE, val) +} + +func testAccCheckDwsClusterDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + client, err := config.sdkClient(OS_REGION_NAME, "dws") + if err != nil { + return fmt.Errorf("Error creating sdk client, err=%s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "huaweicloud_dws_cluster" { + continue + } + + url, err := replaceVarsForTest(rs, "clusters/{id}") + if err != nil { + return err + } + url = client.ServiceURL(url) + + _, err = client.Get( + url, nil, + &golangsdk.RequestOpts{MoreHeaders: map[string]string{"Content-Type": "application/json"}}) + if err == nil { + return fmt.Errorf("huaweicloud_dws_cluster still exists at %s", url) + } + } + + return nil +} + +func testAccCheckDwsClusterExists() resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + client, err := config.sdkClient(OS_REGION_NAME, "dws") + if err != nil { + return fmt.Errorf("Error creating sdk client, err=%s", err) + } + + rs, ok := s.RootModule().Resources["huaweicloud_dws_cluster.cluster"] + if !ok { + return fmt.Errorf("Error checking huaweicloud_dws_cluster.cluster exist, err=not found huaweicloud_dws_cluster.cluster") + } + + url, err := replaceVarsForTest(rs, "clusters/{id}") + if err != nil { + return fmt.Errorf("Error checking huaweicloud_dws_cluster.cluster exist, err=building url failed: %s", err) + } + url = client.ServiceURL(url) + + _, err = client.Get( + url, nil, + &golangsdk.RequestOpts{MoreHeaders: map[string]string{"Content-Type": "application/json"}}) + if err != nil { + if _, ok := err.(golangsdk.ErrDefault404); ok { + return fmt.Errorf("huaweicloud_dws_cluster.cluster is not exist") + } + return fmt.Errorf("Error checking huaweicloud_dws_cluster.cluster exist, err=send request failed: %s", err) + } + return nil + } +} diff --git a/huaweicloud/transport.go b/huaweicloud/transport.go new file mode 100644 index 0000000000..fd578aebb1 --- /dev/null +++ b/huaweicloud/transport.go @@ -0,0 +1,138 @@ +package huaweicloud + +import ( + "fmt" + "net/url" + "reflect" + "regexp" + "strconv" + "strings" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +var successHTTPCodes = []int{200, 201, 202, 203, 204, 205, 206, 207, 208, 226} + +func isEmptyValue(v reflect.Value) (bool, error) { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0, nil + case reflect.Bool: + return !v.Bool(), nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0, nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0, nil + case reflect.Float32, reflect.Float64: + return v.Float() == 0, nil + case reflect.Interface, reflect.Ptr: + return v.IsNil(), nil + case reflect.Invalid: + return true, nil + } + return false, fmt.Errorf("isEmptyValue:: unknown type") +} + +func addQueryParams(rawurl string, params map[string]string) (string, error) { + u, err := url.Parse(rawurl) + if err != nil { + return "", err + } + q := u.Query() + for k, v := range params { + q.Set(k, v) + } + u.RawQuery = q.Encode() + return u.String(), nil +} + +func replaceVars(d *schema.ResourceData, linkTmpl string, kv map[string]string) (string, error) { + re := regexp.MustCompile("{([[:word:]]+)}") + + replaceFunc := func(s string) string { + m := re.FindStringSubmatch(s)[1] + if kv != nil { + if v, ok := kv[m]; ok { + return v + } + } + if m == "project" { + return "replace_holder" + } + if d != nil { + if m == "id" { + return d.Id() + } + v, ok := d.GetOk(m) + if ok { + return v.(string) + } + } + return "" + } + + s := re.ReplaceAllStringFunc(linkTmpl, replaceFunc) + return strings.Replace(s, "replace_holder/", "", 1), nil +} + +func replaceVarsForTest(rs *terraform.ResourceState, linkTmpl string) (string, error) { + re := regexp.MustCompile("{([[:word:]]+)}") + + replaceFunc := func(s string) string { + m := re.FindStringSubmatch(s)[1] + if m == "project" { + return "replace_holder" + } + if rs != nil { + if m == "id" { + return rs.Primary.ID + } + v, ok := rs.Primary.Attributes[m] + if ok { + return v + } + } + return "" + } + + s := re.ReplaceAllStringFunc(linkTmpl, replaceFunc) + return strings.Replace(s, "replace_holder/", "", 1), nil +} + +func navigateMap(d interface{}, index []string) (interface{}, error) { + for _, i := range index { + d1, ok := d.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("navigateMap:: Can not convert to map") + } + d, ok = d1[i] + if !ok { + return nil, fmt.Errorf("navigateMap:: '%s' may not exist", i) + } + } + return d, nil +} + +func convertToInt(v interface{}) (interface{}, error) { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + return strconv.ParseInt(strVal, 10, 64) + } + return nil, fmt.Errorf("can not convert to integer") +} + +func waitToFinish(target, pending []string, timeout, interval time.Duration, f resource.StateRefreshFunc) (interface{}, error) { + stateConf := &resource.StateChangeConf{ + Target: target, + Pending: pending, + Refresh: f, + Timeout: timeout, + Delay: 5 * time.Second, + MinTimeout: interval, + } + + return stateConf.WaitForState() +} diff --git a/vendor/github.com/huaweicloud/golangsdk/openstack/client.go b/vendor/github.com/huaweicloud/golangsdk/openstack/client.go index a256505da7..4ea27d804c 100644 --- a/vendor/github.com/huaweicloud/golangsdk/openstack/client.go +++ b/vendor/github.com/huaweicloud/golangsdk/openstack/client.go @@ -813,3 +813,14 @@ func NewRDSV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*gol sc, err := initClientOpts(client, eo, "rdsv1") return sc, err } + +func NewSDKClient(c *golangsdk.ProviderClient, eo golangsdk.EndpointOpts, serviceType string) (*golangsdk.ServiceClient, error) { + switch serviceType { + case "mls": + return NewMLSV1(c, eo) + case "dws": + return NewDWSClient(c, eo) + } + + return initClientOpts(c, eo, serviceType) +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 05fc54b1dd..e8855add07 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -542,10 +542,10 @@ "revisionTime": "2018-12-24T01:51:05Z" }, { - "checksumSHA1": "HGYxNeP/9WBTL6n6r1L3Eb84Oow=", + "checksumSHA1": "XIukSfoUEQhu9QnfVOmTNYSYhuU=", "path": "github.com/huaweicloud/golangsdk/openstack", - "revision": "f78fa729fe03a6783c80f167ddbfd2b71e7b7151", - "revisionTime": "2018-12-25T07:30:22Z" + "revision": "c6e21652fc257ba67c3f1a2d041c687228356a80", + "revisionTime": "2018-12-29T08:39:20Z" }, { "checksumSHA1": "HVR9NEEAzULt23DLXM3jXxEN4AI=", diff --git a/website/docs/r/dws_cluster.html.markdown b/website/docs/r/dws_cluster.html.markdown new file mode 100644 index 0000000000..a5e81e82c8 --- /dev/null +++ b/website/docs/r/dws_cluster.html.markdown @@ -0,0 +1,199 @@ +--- +layout: "huaweicloud" +page_title: "HuaweiCloud: huaweicloud_dws_cluster" +sidebar_current: "docs-huaweicloud-resource-dws-cluster" +description: |- + cluster management +--- + +# huaweicloud\_dws\_cluster + +cluster management + +## Example Usage + +### Dws Cluster Example + +```hcl +resource "huaweicloud_networking_secgroup_v2" "secgroup" { + name = "security_group_2" + description = "terraform security group" +} + +resource "huaweicloud_dws_cluster" "cluster" { + node_type = "dws.m3.xlarge" + number_of_node = 3 + network_id = "{{ network_id }}" + vpc_id = "{{ vpc_id }}" + security_group_id = "${huaweicloud_networking_secgroup_v2.secgroup.id}" + availability_zone = "{{ availability_zone }}" + name = "terraform_dws_cluster_test" + user_name = "test_cluster_admin" + user_pwd = "cluster123@!" + + timeouts { + create = "30m" + delete = "30m" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - + (Required) + Cluster name, which must be unique and contains 4 to 64 + characters, which consist of letters, digits, hyphens (-), or + underscores (_) only and must start with a letter. + +* `network_id` - + (Required) + Network ID, which is used for configuring cluster network + +* `node_type` - + (Required) + Node type + +* `number_of_node` - + (Required) + Number of nodes in a cluster. The value ranges from 3 to 32 + +* `security_group_id` - + (Required) + ID of a security group. The ID is used for configuring cluster + network + +* `user_name` - + (Required) + Administrator username for logging in to a data warehouse cluster The + administrator username must: Consist of lowercase letters, digits, + or underscores. Start with a lowercase letter or an underscore. + Contain 1 to 63 characters. Cannot be a keyword of the DWS database. + +* `vpc_id` - + (Required) + VPC ID, which is used for configuring cluster network + +* `user_pwd` - + (Required) + Administrator password for logging in to a data warehouse cluster A + password must conform to the following rules: Contains 8 to 32 + characters. Cannot be the same as the username or the username + written in reverse order. Contains three types of the following: + Lowercase letters Uppercase letters Digits Special characters + ~!@#%^&*()-_=+|[{}];:,<.>/? + +- - - + +* `availability_zone` - + (Optional) + AZ in a cluster + +* `port` - + (Optional) + Service port of a cluster (8000 to 10000). The default value is + 8000 + +* `public_ip` - + (Optional) + A nested object resource Structure is documented below. + +The `public_ip` block supports: + +* `eip_id` - + (Optional) + EIP ID + +* `public_bind_type` - + (Optional) + Binding type of an EIP. The value can be either of the following: + auto_assign not_use bind_existing The default value is + not_use. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `created` - + Cluster creation time. The format is ISO8601:YYYY-MM-DDThh:mm:ssZ + +* `endpoints` - + View the private network connection information about the + cluster. Structure is documented below. + +* `id` - + Cluster ID + +* `public_endpoints` - + Public network connection information about the cluster. If the + value is not specified, the public network connection information is + not used by default Structure is documented below. + +* `recent_event` - + 事件数 + +* `status` - + Cluster status, which can be one of the following: CREATING + AVAILABLE UNAVAILABLE CREATION FAILED + +* `sub_status` - + Sub-status of clusters in the AVAILABLE state. The value can be one + of the following: NORMAL READONLY REDISTRIBUTING + REDISTRIBUTION-FAILURE UNBALANCED UNBALANCED | READONLY DEGRADED + DEGRADED | READONLY DEGRADED | UNBALANCED UNBALANCED | + REDISTRIBUTING UNBALANCED | REDISTRIBUTION-FAILURE READONLY | + REDISTRIBUTION-FAILURE UNBALANCED | READONLY | + REDISTRIBUTION-FAILURE DEGRADED | REDISTRIBUTION-FAILURE DEGRADED | + UNBALANCED | REDISTRIBUTION-FAILURE DEGRADED | UNBALANCED | READONLY + | REDISTRIBUTION-FAILURE DEGRADED | UNBALANCED | READONLY + +* `task_status` - + Cluster management task. The value can be one of the following: + RESTORING SNAPSHOTTING GROWING REBOOTING SETTING_CONFIGURATION + CONFIGURING_EXT_DATASOURCE DELETING_EXT_DATASOURCE REBOOT_FAILURE + RESIZE_FAILURE + +* `updated` - + Last modification time of a cluster. The format is + ISO8601:YYYY-MM-DDThh:mm:ssZ + +* `version` - + Data warehouse version + +The `endpoints` block contains: + +* `connect_info` - + (Optional) + Private network connection information + +* `jdbc_url` - + (Optional) + JDBC URL. The following is the default format: + jdbc:postgresql://< connect_info>/ + +The `public_endpoints` block contains: + +* `jdbc_url` - + (Optional) + JDBC URL. The following is the default format: + jdbc:postgresql://< public_connect_info>/ + +* `public_connect_info` - + (Optional) + Public network connection information + +## Timeouts + +This resource provides the following timeouts configuration options: +- `create` - Default is 10 minute. +- `delete` - Default is 10 minute. + +## Import + +Cluster can be imported using the following format: + +``` +$ terraform import huaweicloud_dws_cluster.default {{ resource id}} +``` diff --git a/website/huaweicloud.erb b/website/huaweicloud.erb index a73e3c415c..89f06eb9e7 100644 --- a/website/huaweicloud.erb +++ b/website/huaweicloud.erb @@ -458,6 +458,15 @@ + + > + DWS Resources Resources + + <% end %>