alicloud/resource_alicloud_cs_serverless_kubernetes.go (625 lines of code) (raw):

package alicloud import ( "encoding/json" "regexp" "strings" "time" roacs "github.com/alibabacloud-go/cs-20151215/v5/client" "github.com/alibabacloud-go/tea/tea" "github.com/aliyun/terraform-provider-alicloud/alicloud/connectivity" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) func resourceAlicloudCSServerlessKubernetes() *schema.Resource { return &schema.Resource{ Create: resourceAlicloudCSServerlessKubernetesCreate, Read: resourceAlicloudCSServerlessKubernetesRead, Update: resourceAlicloudCSServerlessKubernetesUpdate, Delete: resourceAlicloudCSServerlessKubernetesDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(60 * time.Minute), Delete: schema.DefaultTimeout(30 * time.Minute), }, Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, Optional: true, Computed: true, ValidateFunc: StringLenBetween(1, 63), ConflictsWith: []string{"name_prefix"}, }, "name_prefix": { Type: schema.TypeString, Optional: true, ValidateFunc: StringLenBetween(0, 37), ConflictsWith: []string{"name"}, }, "vpc_id": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, ConflictsWith: []string{"zone_id"}, }, "vswitch_id": { Type: schema.TypeString, Optional: true, Computed: true, Removed: "Field 'vswitch_id' has been removed from provider version 1.229.1. New field 'vswitch_ids' replace it.", }, "vswitch_ids": { Type: schema.TypeList, Optional: true, ForceNew: true, Computed: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: StringMatch(regexp.MustCompile(`^vsw-[a-z0-9]*$`), "should start with 'vsw-'."), }, MinItems: 1, ConflictsWith: []string{"zone_id"}, }, "service_cidr": { Type: schema.TypeString, Optional: true, ForceNew: true, }, "new_nat_gateway": { Type: schema.TypeBool, Optional: true, Default: true, }, "deletion_protection": { Type: schema.TypeBool, Optional: true, Default: false, }, "enable_rrsa": { Type: schema.TypeBool, Optional: true, }, "private_zone": { Type: schema.TypeBool, Optional: true, ConflictsWith: []string{"service_discovery_types"}, Deprecated: "Field 'private_zone' has been deprecated from provider version 1.123.1. New field 'service_discovery_types' replace it.", }, "service_discovery_types": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: StringInSlice([]string{"CoreDNS", "PrivateZone"}, false), }, ConflictsWith: []string{"private_zone"}, }, "zone_id": { Type: schema.TypeString, Optional: true, ConflictsWith: []string{"vpc_id", "vswitch_ids", "vswitch_id"}, }, "endpoint_public_access_enabled": { Type: schema.TypeBool, Optional: true, ForceNew: true, Default: false, }, "kube_config": { Type: schema.TypeString, Optional: true, Deprecated: "Field 'kube_config' has been deprecated from provider version 1.187.0. Please use the attribute 'output_file' of new DataSource 'alicloud_cs_cluster_credential' to replace it.", }, "client_cert": { Type: schema.TypeString, Optional: true, Deprecated: "Field 'client_cert' has been deprecated from provider version 1.248.0. From version 1.248.0, new DataSource 'alicloud_cs_cluster_credential' is recommended to manage cluster's kubeconfig, you can also save the 'certificate_authority.client_cert' attribute content of new DataSource 'alicloud_cs_cluster_credential' to an appropriate path(like ~/.kube/client-cert.pem) for replace it.", }, "client_key": { Type: schema.TypeString, Optional: true, Deprecated: "Field 'client_key' has been deprecated from provider version 1.248.0. From version 1.248.0, new DataSource 'alicloud_cs_cluster_credential' is recommended to manage cluster's kubeconfig, you can also save the 'certificate_authority.client_key' attribute content of new DataSource 'alicloud_cs_cluster_credential' to an appropriate path(like ~/.kube/client-key.pem) for replace it.", }, "cluster_ca_cert": { Type: schema.TypeString, Optional: true, Deprecated: "Field 'cluster_ca_cert' has been deprecated from provider version 1.248.0. From version 1.248.0, new DataSource 'alicloud_cs_cluster_credential' is recommended to manage cluster's kubeconfig, you can also save the 'certificate_authority.cluster_cert' attribute content of new DataSource 'alicloud_cs_cluster_credential' to an appropriate path(like ~/.kube/cluster-ca-cert.pem) for replace it.", }, "tags": { Type: schema.TypeMap, Optional: true, }, "force_update": { Type: schema.TypeBool, Optional: true, Removed: "Field 'force_update' has been removed from provider version 1.229.1.", }, "security_group_id": { Type: schema.TypeString, Optional: true, ForceNew: true, Computed: true, }, "addons": { Type: schema.TypeList, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, Optional: true, }, "config": { Type: schema.TypeString, Optional: true, }, "version": { Type: schema.TypeString, Optional: true, }, "disabled": { Type: schema.TypeBool, Optional: true, Default: false, }, }, }, }, "version": { Type: schema.TypeString, Optional: true, Computed: true, }, "resource_group_id": { Type: schema.TypeString, Optional: true, Computed: true, }, "load_balancer_spec": { Type: schema.TypeString, Optional: true, Computed: true, ValidateFunc: StringInSlice([]string{"slb.s1.small", "slb.s2.small", "slb.s2.medium", "slb.s3.small", "slb.s3.medium", "slb.s3.large"}, false), Deprecated: "Field 'load_balancer_spec' has been deprecated from provider version 1.229.1. The load balancer has been changed to PayByCLCU so that the spec is no need anymore.", }, "logging_type": { Type: schema.TypeString, Optional: true, Default: "SLS", Deprecated: "Field 'logging_type' has been deprecated from provider version 1.229.1. Please use addons `alibaba-log-controller` to enable logging.", }, "sls_project_name": { Type: schema.TypeString, Optional: true, Computed: true, Deprecated: "Field 'sls_project_name' has been deprecated from provider version 1.229.1. Please use the field `config` of addons `alibaba-log-controller` to specify log project name.", }, "time_zone": { Type: schema.TypeString, Optional: true, Computed: true, }, "retain_resources": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, }, "cluster_spec": { Type: schema.TypeString, Optional: true, Computed: true, ValidateFunc: StringInSlice([]string{"ack.standard", "ack.pro.small"}, false), }, "create_v2_cluster": { Type: schema.TypeBool, Optional: true, Computed: true, Removed: "Field 'create_v2_cluster' has been removed from provider version 1.229.1.", }, "rrsa_metadata": { Type: schema.TypeList, Computed: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "enabled": { Type: schema.TypeBool, Computed: true, }, "rrsa_oidc_issuer_url": { Type: schema.TypeString, Computed: true, }, "ram_oidc_provider_name": { Type: schema.TypeString, Computed: true, }, "ram_oidc_provider_arn": { Type: schema.TypeString, Computed: true, }, }, }, }, "custom_san": { Type: schema.TypeString, Optional: true, }, "delete_options": { Type: schema.TypeList, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "resource_type": { Type: schema.TypeString, Optional: true, ValidateFunc: StringInSlice([]string{"SLB", "ALB", "SLS_Data", "SLS_ControlPlane", "PrivateZone"}, false), }, "delete_mode": { Type: schema.TypeString, Optional: true, ValidateFunc: StringInSlice([]string{"delete", "retain"}, false), }, }, }, }, "maintenance_window": { Type: schema.TypeList, Optional: true, Computed: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "enable": { Type: schema.TypeBool, Optional: true, Computed: true, }, "maintenance_time": { Type: schema.TypeString, Optional: true, Computed: true, }, "duration": { Type: schema.TypeString, Optional: true, Computed: true, }, "weekly_period": { Type: schema.TypeString, Optional: true, Computed: true, }, }, }, }, "operation_policy": { Type: schema.TypeList, Optional: true, Computed: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "cluster_auto_upgrade": { Type: schema.TypeList, Optional: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "enabled": { Type: schema.TypeBool, Optional: true, }, "channel": { Type: schema.TypeString, Optional: true, }, }, }, }, }, }, }, }, } } func resourceAlicloudCSServerlessKubernetesCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*connectivity.AliyunClient) roa, _ := client.NewRoaCsClient() csClient := CsClient{roa} var clusterName string if v, ok := d.GetOk("name"); ok { clusterName = v.(string) } else { clusterName = resource.PrefixedUniqueId(d.Get("name_prefix").(string)) } tags := make([]*roacs.Tag, 0) tagsMap, ok := d.Get("tags").(map[string]interface{}) if ok { for key, value := range tagsMap { if value != nil { if v, ok := value.(string); ok { tags = append(tags, &roacs.Tag{ Key: tea.String(key), Value: tea.String(v), }) } } } } addons := make([]*roacs.Addon, 0) if v, ok := d.GetOk("addons"); ok { all, ok := v.([]interface{}) if ok { for _, a := range all { addon, ok := a.(map[string]interface{}) if ok { addons = append(addons, &roacs.Addon{ Name: tea.String(addon["name"].(string)), Config: tea.String(addon["config"].(string)), Version: tea.String(addon["version"].(string)), Disabled: tea.Bool(addon["disabled"].(bool)), }) } } } } if v, ok := d.GetOk("sls_project_name"); ok { exist := false for _, addon := range addons { if tea.StringValue(addon.Name) == "alibaba-log-controller" { var config map[string]interface{} err := json.Unmarshal([]byte(tea.StringValue(addon.Config)), &config) if err == nil { if project, ok := config["sls_project_name"].(string); !ok || project == "" { config["sls_project_name"] = v.(string) } if configRaw, err := json.Marshal(config); err == nil { addon.Config = tea.String(string(configRaw)) } } exist = true break } } if exist == false { config := map[string]interface{}{ "sls_project_name": v.(string), } if configRaw, err := json.Marshal(config); err == nil { addons = append(addons, &roacs.Addon{ Name: tea.String("alibaba-log-controller"), Config: tea.String(string(configRaw)), }) } } } request := &roacs.CreateClusterRequest{ Name: tea.String(clusterName), KubernetesVersion: tea.String(d.Get("version").(string)), ClusterType: tea.String("ManagedKubernetes"), Profile: tea.String("Serverless"), Tags: tags, Addons: addons, DeletionProtection: tea.Bool(d.Get("deletion_protection").(bool)), RegionId: tea.String(client.RegionId), ResourceGroupId: tea.String(d.Get("resource_group_id").(string)), Vpcid: tea.String(d.Get("vpc_id").(string)), SnatEntry: tea.Bool(d.Get("new_nat_gateway").(bool)), NatGateway: tea.Bool(d.Get("new_nat_gateway").(bool)), EndpointPublicAccess: tea.Bool(d.Get("endpoint_public_access_enabled").(bool)), SecurityGroupId: tea.String(d.Get("security_group_id").(string)), LoggingType: tea.String(d.Get("logging_type").(string)), } if v, ok := d.GetOk("time_zone"); ok { request.Timezone = tea.String(v.(string)) } if v, ok := d.GetOk("zone_id"); ok { request.ZoneId = tea.String(v.(string)) } if v, ok := d.GetOk("service_cidr"); ok { request.ServiceCidr = tea.String(v.(string)) } if v, ok := d.GetOk("service_discovery_types"); ok { r := make([]*string, 0) for _, vv := range v.([]interface{}) { r = append(r, tea.String(vv.(string))) } request.ServiceDiscoveryTypes = r } if v, ok := d.GetOkExists("private_zone"); ok { request.ServiceDiscoveryTypes = []*string{} if v.(bool) == true { request.ServiceDiscoveryTypes = []*string{tea.String("PrivateZone")} } } if v, ok := d.GetOk("vswitch_ids"); ok { r := make([]*string, 0) for _, vv := range v.([]interface{}) { r = append(r, tea.String(vv.(string))) } request.VswitchIds = r } if v, ok := d.GetOk("load_balancer_spec"); ok { request.LoadBalancerSpec = tea.String(v.(string)) } if v, ok := d.GetOk("cluster_spec"); ok { request.ClusterSpec = tea.String(v.(string)) } if v, ok := d.GetOk("enable_rrsa"); ok { request.EnableRrsa = tea.Bool(v.(bool)) } if v, ok := d.GetOk("custom_san"); ok { request.CustomSan = tea.String(v.(string)) } if v, ok := d.GetOk("maintenance_window"); ok { request.MaintenanceWindow = expandMaintenanceWindowConfigRoa(v.([]interface{})) } if v, ok := d.GetOk("operation_policy"); ok { request.OperationPolicy = &roacs.CreateClusterRequestOperationPolicy{} m := v.([]interface{})[0].(map[string]interface{}) if vv, ok := m["cluster_auto_upgrade"]; ok { policy := vv.([]interface{})[0].(map[string]interface{}) request.OperationPolicy.ClusterAutoUpgrade = &roacs.CreateClusterRequestOperationPolicyClusterAutoUpgrade{ Enabled: tea.Bool(policy["enabled"].(bool)), Channel: tea.String(policy["channel"].(string)), } } } //set tags if len(tags) > 0 { request.Tags = tags } var err error var resp *roacs.CreateClusterResponse wait := incrementalWait(3*time.Second, 3*time.Second) err = resource.Retry(5*time.Minute, func() *resource.RetryError { resp, err = csClient.client.CreateCluster(request) if err != nil { if NeedRetry(err) { wait() return resource.RetryableError(err) } return resource.NonRetryableError(err) } return nil }) if err != nil { return WrapErrorf(err, DefaultErrorMsg, "alicloud_cs_serverless_kubernetes", "CreateServerlessKubernetesCluster", AlibabaCloudSdkGoERROR) } d.SetId(tea.StringValue(resp.Body.ClusterId)) taskId := tea.StringValue(resp.Body.TaskId) stateConf := BuildStateConf([]string{}, []string{"success"}, d.Timeout(schema.TimeoutCreate), 10*time.Second, csClient.DescribeTaskRefreshFunc(d, taskId, []string{"fail", "failed"})) if jobDetail, err := stateConf.WaitForState(); err != nil { return WrapErrorf(err, ResponseCodeMsg, d.Id(), "createCluster", jobDetail) } csService := CsService{client} stateConf = BuildStateConf([]string{"initial"}, []string{"running"}, d.Timeout(schema.TimeoutCreate), 30*time.Second, csService.CsServerlessKubernetesInstanceStateRefreshFunc(d.Id(), []string{"deleting", "failed"})) if _, err := stateConf.WaitForState(); err != nil { return WrapErrorf(err, IdMsg, d.Id()) } return resourceAlicloudCSServerlessKubernetesRead(d, meta) } func resourceAlicloudCSServerlessKubernetesRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*connectivity.AliyunClient) rosClient, err := client.NewRoaCsClient() if err != nil { return WrapErrorf(err, DefaultErrorMsg, ResourceName, "InitializeClient", err) } csClient := CsClient{rosClient} object, err := csClient.DescribeClusterDetail(d.Id()) if err != nil { if NotFoundError(err) { d.SetId("") return nil } return WrapError(err) } vswitchIds := []string{} request := &roacs.DescribeClusterResourcesRequest{} resources, _ := rosClient.DescribeClusterResources(tea.String(d.Id()), request) for _, resource := range resources.Body { if tea.StringValue(resource.ResourceType) == "VSWITCH" { vswitchIds = append(vswitchIds, tea.StringValue(resource.InstanceId)) } } d.Set("name", object.Name) d.Set("vpc_id", object.VpcId) d.Set("vswitch_ids", vswitchIds) d.Set("security_group_id", object.SecurityGroupId) d.Set("deletion_protection", object.DeletionProtection) d.Set("version", object.CurrentVersion) d.Set("resource_group_id", object.ResourceGroupId) d.Set("cluster_spec", object.ClusterSpec) if object.Timezone != nil { d.Set("timezone", object.Timezone) } if err := d.Set("tags", flattenTags(object.Tags)); err != nil { return WrapError(err) } if d.Get("load_balancer_spec") == "" { d.Set("load_balancer_spec", "slb.s2.small") } if d.Get("logging_type") == "" { d.Set("logging_type", "SLS") } if object.ServiceCidr != nil { d.Set("service_cidr", object.ServiceCidr) } else { if v, ok := object.Parameters["ServiceCIDR"]; ok { d.Set("service_cidr", v) } } capabilities := fetchClusterCapabilities(tea.StringValue(object.MetaData)) if v, ok := capabilities["PublicSLB"]; ok { d.Set("endpoint_public_access_enabled", Interface2Bool(v)) } metadata := fetchClusterMetaDataMap(tea.StringValue(object.MetaData)) if v, ok := metadata["ExtraCertSAN"]; ok && v != nil { l := expandStringList(v.([]interface{})) d.Set("custom_san", strings.Join(l, ",")) } if data, err := flattenRRSAMetadata(tea.StringValue(object.MetaData)); err != nil { return WrapError(err) } else { d.Set("rrsa_metadata", data) if len(data) > 0 { d.Set("enable_rrsa", data[0]["enabled"].(bool)) } } if object.MaintenanceWindow != nil { d.Set("maintenance_window", flattenMaintenanceWindowConfigRoa(object.MaintenanceWindow)) } if object.OperationPolicy != nil { m := make([]map[string]interface{}, 0) if object.OperationPolicy.ClusterAutoUpgrade != nil { m = append(m, map[string]interface{}{ "cluster_auto_upgrade": []map[string]interface{}{ { "enabled": tea.BoolValue(object.OperationPolicy.ClusterAutoUpgrade.Enabled), "channel": tea.StringValue(object.OperationPolicy.ClusterAutoUpgrade.Channel), }, }, }) } d.Set("operation_policy", m) } // get cluster conn certs // If the cluster is failed, there is no need to get cluster certs if tea.StringValue(object.State) == "failed" || tea.StringValue(object.State) == "delete_failed" || tea.StringValue(object.State) == "deleting" { return nil } if err = setCerts(d, meta, true); err != nil { return WrapError(err) } return nil } func resourceAlicloudCSServerlessKubernetesUpdate(d *schema.ResourceData, meta interface{}) error { invoker := NewInvoker() // modifyCluster if !d.IsNewResource() && d.HasChanges("resource_group_id", "name", "name_prefix", "deletion_protection", "custom_san", "maintenance_window", "operation_policy", "enable_rrsa") { if err := modifyCluster(d, meta, &invoker); err != nil { return WrapErrorf(err, DefaultErrorMsg, d.Id(), "ModifyCluster", AlibabaCloudSdkGoERROR) } } // modify cluster tag if d.HasChange("tags") { err := updateKubernetesClusterTag(d, meta) if err != nil { return WrapErrorf(err, ResponseCodeMsg, d.Id(), "ModifyClusterTags", AlibabaCloudSdkGoERROR) } } // migrate cluster to pro form standard if d.HasChange("cluster_spec") { err := migrateCluster(d, meta) if err != nil { return WrapError(err) } } // upgrade cluster err := UpgradeAlicloudKubernetesCluster(d, meta) if err != nil { return WrapError(err) } return resourceAlicloudCSServerlessKubernetesRead(d, meta) } func resourceAlicloudCSServerlessKubernetesDelete(d *schema.ResourceData, meta interface{}) error { return resourceAlicloudCSKubernetesDelete(d, meta) }