From daeb3050311baca8639a6e8842b736dadf58990d Mon Sep 17 00:00:00 2001 From: "maoshuai.17" Date: Tue, 20 Aug 2024 15:44:56 +0800 Subject: [PATCH 1/2] feat: support vepfs --- example/dataVepfsFileSystems/main.tf | 29 + example/dataVepfsFilesets/main.tf | 40 ++ example/dataVepfsMountServices/main.tf | 22 + example/vepfsFileSystem/main.tf | 25 + example/vepfsFileset/main.tf | 35 ++ example/vepfsMountService/main.tf | 18 + example/vepfsMountServiceAttachment/main.tf | 37 ++ volcengine/provider.go | 16 + ...ta_source_volcengine_vepfs_file_systems.go | 262 +++++++++ .../resource_volcengine_vepfs_file_system.go | 206 +++++++ .../service_volcengine_vepfs_file_system.go | 538 +++++++++++++++++ .../data_source_volcengine_vepfs_filesets.go | 140 +++++ .../resource_volcengine_vepfs_fileset.go | 154 +++++ .../service_volcengine_vepfs_fileset.go | 539 ++++++++++++++++++ ..._source_volcengine_vepfs_mount_services.go | 184 ++++++ ...resource_volcengine_vepfs_mount_service.go | 190 ++++++ .../service_volcengine_vepfs_mount_service.go | 383 +++++++++++++ ...lcengine_vepfs_mount_service_attachment.go | 114 ++++ ...lcengine_vepfs_mount_service_attachment.go | 321 +++++++++++ 19 files changed, 3253 insertions(+) create mode 100644 example/dataVepfsFileSystems/main.tf create mode 100644 example/dataVepfsFilesets/main.tf create mode 100644 example/dataVepfsMountServices/main.tf create mode 100644 example/vepfsFileSystem/main.tf create mode 100644 example/vepfsFileset/main.tf create mode 100644 example/vepfsMountService/main.tf create mode 100644 example/vepfsMountServiceAttachment/main.tf create mode 100644 volcengine/vepfs/vepfs_file_system/data_source_volcengine_vepfs_file_systems.go create mode 100644 volcengine/vepfs/vepfs_file_system/resource_volcengine_vepfs_file_system.go create mode 100644 volcengine/vepfs/vepfs_file_system/service_volcengine_vepfs_file_system.go create mode 100644 volcengine/vepfs/vepfs_fileset/data_source_volcengine_vepfs_filesets.go create mode 100644 volcengine/vepfs/vepfs_fileset/resource_volcengine_vepfs_fileset.go create mode 100644 volcengine/vepfs/vepfs_fileset/service_volcengine_vepfs_fileset.go create mode 100644 volcengine/vepfs/vepfs_mount_service/data_source_volcengine_vepfs_mount_services.go create mode 100644 volcengine/vepfs/vepfs_mount_service/resource_volcengine_vepfs_mount_service.go create mode 100644 volcengine/vepfs/vepfs_mount_service/service_volcengine_vepfs_mount_service.go create mode 100644 volcengine/vepfs/vepfs_mount_service_attachment/resource_volcengine_vepfs_mount_service_attachment.go create mode 100644 volcengine/vepfs/vepfs_mount_service_attachment/service_volcengine_vepfs_mount_service_attachment.go diff --git a/example/dataVepfsFileSystems/main.tf b/example/dataVepfsFileSystems/main.tf new file mode 100644 index 0000000..fe0414f --- /dev/null +++ b/example/dataVepfsFileSystems/main.tf @@ -0,0 +1,29 @@ +resource "volcengine_vpc" "foo" { + vpc_name = "acc-test-vpc" + cidr_block = "172.16.0.0/16" +} + +resource "volcengine_subnet" "foo" { + subnet_name = "acc-test-subnet" + cidr_block = "172.16.0.0/24" + zone_id = "cn-beijing-a" + vpc_id = volcengine_vpc.foo.id +} + +resource "volcengine_vepfs_file_system" "foo" { + file_system_name = "acc-test-file-system" + subnet_id = volcengine_subnet.foo.id + store_type = "Advance_100" + description = "tf-test" + capacity = 12 + project = "default" + enable_restripe = false + tags { + key = "k1" + value = "v1" + } +} + +data "volcengine_vepfs_file_systems" "foo" { + ids = [volcengine_vepfs_file_system.foo.id] +} diff --git a/example/dataVepfsFilesets/main.tf b/example/dataVepfsFilesets/main.tf new file mode 100644 index 0000000..fc4dafa --- /dev/null +++ b/example/dataVepfsFilesets/main.tf @@ -0,0 +1,40 @@ +resource "volcengine_vpc" "foo" { + vpc_name = "acc-test-vpc" + cidr_block = "172.16.0.0/16" +} + +resource "volcengine_subnet" "foo" { + subnet_name = "acc-test-subnet" + cidr_block = "172.16.0.0/24" + zone_id = "cn-beijing-a" + vpc_id = volcengine_vpc.foo.id +} + +resource "volcengine_vepfs_file_system" "foo" { + file_system_name = "acc-test-file-system" + subnet_id = volcengine_subnet.foo.id + store_type = "Advance_100" + description = "tf-test" + capacity = 12 + project = "default" + enable_restripe = false + tags { + key = "k1" + value = "v1" + } +} + +resource "volcengine_vepfs_fileset" "foo" { + file_system_id = volcengine_vepfs_file_system.foo.id + fileset_name = "acc-test-fileset" + fileset_path = "/tf-test/" + max_iops = 100 + max_bandwidth = 10 + file_limit = 20 + capacity_limit = 30 +} + +data "volcengine_vepfs_filesets" "foo" { + file_system_id = volcengine_vepfs_file_system.foo.id + fileset_id = volcengine_vepfs_fileset.foo.id +} diff --git a/example/dataVepfsMountServices/main.tf b/example/dataVepfsMountServices/main.tf new file mode 100644 index 0000000..13a67ce --- /dev/null +++ b/example/dataVepfsMountServices/main.tf @@ -0,0 +1,22 @@ +resource "volcengine_vpc" "foo" { + vpc_name = "acc-test-vpc" + cidr_block = "172.16.0.0/16" +} + +resource "volcengine_subnet" "foo" { + subnet_name = "acc-test-subnet" + cidr_block = "172.16.0.0/24" + zone_id = "cn-beijing-a" + vpc_id = volcengine_vpc.foo.id +} + +resource "volcengine_vepfs_mount_service" "foo" { + mount_service_name = "acc-test-mount-service" + subnet_id = volcengine_subnet.foo.id + node_type = "ecs.g1ie.large" + project = "default" +} + +data "volcengine_vepfs_mount_services" "foo" { + mount_service_id = volcengine_vepfs_mount_service.foo.id +} diff --git a/example/vepfsFileSystem/main.tf b/example/vepfsFileSystem/main.tf new file mode 100644 index 0000000..5f224fd --- /dev/null +++ b/example/vepfsFileSystem/main.tf @@ -0,0 +1,25 @@ +resource "volcengine_vpc" "foo" { + vpc_name = "acc-test-vpc" + cidr_block = "172.16.0.0/16" +} + +resource "volcengine_subnet" "foo" { + subnet_name = "acc-test-subnet" + cidr_block = "172.16.0.0/24" + zone_id = "cn-beijing-a" + vpc_id = volcengine_vpc.foo.id +} + +resource "volcengine_vepfs_file_system" "foo" { + file_system_name = "acc-test-file-system" + subnet_id = volcengine_subnet.foo.id + store_type = "Advance_100" + description = "tf-test" + capacity = 12 + project = "default" + enable_restripe = false + tags { + key = "k1" + value = "v1" + } +} diff --git a/example/vepfsFileset/main.tf b/example/vepfsFileset/main.tf new file mode 100644 index 0000000..e593649 --- /dev/null +++ b/example/vepfsFileset/main.tf @@ -0,0 +1,35 @@ +resource "volcengine_vpc" "foo" { + vpc_name = "acc-test-vpc" + cidr_block = "172.16.0.0/16" +} + +resource "volcengine_subnet" "foo" { + subnet_name = "acc-test-subnet" + cidr_block = "172.16.0.0/24" + zone_id = "cn-beijing-a" + vpc_id = volcengine_vpc.foo.id +} + +resource "volcengine_vepfs_file_system" "foo" { + file_system_name = "acc-test-file-system" + subnet_id = volcengine_subnet.foo.id + store_type = "Advance_100" + description = "tf-test" + capacity = 12 + project = "default" + enable_restripe = false + tags { + key = "k1" + value = "v1" + } +} + +resource "volcengine_vepfs_fileset" "foo" { + file_system_id = volcengine_vepfs_file_system.foo.id + fileset_name = "acc-test-fileset" + fileset_path = "/tf-test/" + max_iops = 100 + max_bandwidth = 10 + file_limit = 20 + capacity_limit = 30 +} diff --git a/example/vepfsMountService/main.tf b/example/vepfsMountService/main.tf new file mode 100644 index 0000000..2484790 --- /dev/null +++ b/example/vepfsMountService/main.tf @@ -0,0 +1,18 @@ +resource "volcengine_vpc" "foo" { + vpc_name = "acc-test-vpc" + cidr_block = "172.16.0.0/16" +} + +resource "volcengine_subnet" "foo" { + subnet_name = "acc-test-subnet" + cidr_block = "172.16.0.0/24" + zone_id = "cn-beijing-a" + vpc_id = volcengine_vpc.foo.id +} + +resource "volcengine_vepfs_mount_service" "foo" { + mount_service_name = "acc-test-mount-service" + subnet_id = volcengine_subnet.foo.id + node_type = "ecs.g1ie.large" + project = "default" +} diff --git a/example/vepfsMountServiceAttachment/main.tf b/example/vepfsMountServiceAttachment/main.tf new file mode 100644 index 0000000..814f751 --- /dev/null +++ b/example/vepfsMountServiceAttachment/main.tf @@ -0,0 +1,37 @@ +resource "volcengine_vpc" "foo" { + vpc_name = "acc-test-vpc" + cidr_block = "172.16.0.0/16" +} + +resource "volcengine_subnet" "foo" { + subnet_name = "acc-test-subnet" + cidr_block = "172.16.0.0/24" + zone_id = "cn-beijing-a" + vpc_id = volcengine_vpc.foo.id +} + +resource "volcengine_vepfs_file_system" "foo" { + file_system_name = "acc-test-file-system" + subnet_id = volcengine_subnet.foo.id + store_type = "Advance_100" + description = "tf-test" + capacity = 12 + project = "default" + enable_restripe = false + tags { + key = "k1" + value = "v1" + } +} + +resource "volcengine_vepfs_mount_service" "foo" { + mount_service_name = "acc-test-mount-service" + subnet_id = volcengine_subnet.foo.id + node_type = "ecs.g1ie.large" + project = "default" +} + +resource "volcengine_vepfs_mount_service_attachment" "foo" { + mount_service_id = volcengine_vepfs_mount_service.foo.id + file_system_id = volcengine_vepfs_file_system.foo.id +} diff --git a/volcengine/provider.go b/volcengine/provider.go index 400d518..2c98347 100644 --- a/volcengine/provider.go +++ b/volcengine/provider.go @@ -10,6 +10,11 @@ import ( "strings" "time" + "github.com/volcengine/terraform-provider-volcengine/volcengine/vepfs/vepfs_file_system" + "github.com/volcengine/terraform-provider-volcengine/volcengine/vepfs/vepfs_fileset" + "github.com/volcengine/terraform-provider-volcengine/volcengine/vepfs/vepfs_mount_service" + "github.com/volcengine/terraform-provider-volcengine/volcengine/vepfs/vepfs_mount_service_attachment" + "github.com/volcengine/terraform-provider-volcengine/volcengine/private_zone/private_zone" "github.com/volcengine/terraform-provider-volcengine/volcengine/private_zone/private_zone_record" "github.com/volcengine/terraform-provider-volcengine/volcengine/private_zone/private_zone_record_set" @@ -683,6 +688,11 @@ func Provider() terraform.ResourceProvider { "volcengine_private_zone_resolver_rules": private_zone_resolver_rule.DataSourceVolcenginePrivateZoneResolverRules(), "volcengine_private_zone_records": private_zone_record.DataSourceVolcenginePrivateZoneRecords(), "volcengine_private_zone_record_sets": private_zone_record_set.DataSourceVolcenginePrivateZoneRecordSets(), + + // ================ Vepfs ================ + "volcengine_vepfs_file_systems": vepfs_file_system.DataSourceVolcengineVepfsFileSystems(), + "volcengine_vepfs_mount_services": vepfs_mount_service.DataSourceVolcengineVepfsMountServices(), + "volcengine_vepfs_filesets": vepfs_fileset.DataSourceVolcengineVepfsFilesets(), }, ResourcesMap: map[string]*schema.Resource{ "volcengine_vpc": vpc.ResourceVolcengineVpc(), @@ -977,6 +987,12 @@ func Provider() terraform.ResourceProvider { "volcengine_private_zone_record": private_zone_record.ResourceVolcenginePrivateZoneRecord(), "volcengine_private_zone_record_weight_enabler": private_zone_record_weight_enabler.ResourceVolcenginePrivateZoneRecordWeightEnabler(), "volcengine_private_zone_user_vpc_authorization": private_zone_user_vpc_authorization.ResourceVolcenginePrivateZoneUserVpcAuthorization(), + + // ================ Vepfs ================ + "volcengine_vepfs_file_system": vepfs_file_system.ResourceVolcengineVepfsFileSystem(), + "volcengine_vepfs_mount_service": vepfs_mount_service.ResourceVolcengineVepfsMountService(), + "volcengine_vepfs_mount_service_attachment": vepfs_mount_service_attachment.ResourceVolcengineVepfsMountServiceAttachment(), + "volcengine_vepfs_fileset": vepfs_fileset.ResourceVolcengineVepfsFileset(), }, ConfigureFunc: ProviderConfigure, } diff --git a/volcengine/vepfs/vepfs_file_system/data_source_volcengine_vepfs_file_systems.go b/volcengine/vepfs/vepfs_file_system/data_source_volcengine_vepfs_file_systems.go new file mode 100644 index 0000000..e8a1793 --- /dev/null +++ b/volcengine/vepfs/vepfs_file_system/data_source_volcengine_vepfs_file_systems.go @@ -0,0 +1,262 @@ +package vepfs_file_system + +import ( + "bytes" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/helper/hashcode" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + ve "github.com/volcengine/terraform-provider-volcengine/common" + "strings" +) + +func DataSourceVolcengineVepfsFileSystems() *schema.Resource { + return &schema.Resource{ + Read: dataSourceVolcengineVepfsFileSystemsRead, + Schema: map[string]*schema.Schema{ + "ids": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Set: schema.HashString, + Description: "A list of Vepfs File System IDs.", + }, + "store_type": { + Type: schema.TypeString, + Optional: true, + Description: "The Store Type of Vepfs File System.", + }, + "file_system_name": { + Type: schema.TypeString, + Optional: true, + Description: "The Name of Vepfs File System. This field support fuzzy query.", + }, + "status": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Set: schema.HashString, + Description: "The query status list of Vepfs File System.", + }, + "zone_id": { + Type: schema.TypeString, + Optional: true, + Description: "The zone id of File System.", + }, + "project": { + Type: schema.TypeString, + Optional: true, + Description: "The project of Vepfs File System.", + }, + "name_regex": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringIsValidRegExp, + Description: "A Name Regex of Resource.", + }, + "output_file": { + Type: schema.TypeString, + Optional: true, + Description: "File name where to save data source results.", + }, + "total_count": { + Type: schema.TypeInt, + Computed: true, + Description: "The total count of query.", + }, + + "file_systems": { + Description: "The collection of query.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the vepfs file system.", + }, + "file_system_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the vepfs file system.", + }, + "file_system_name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the vepfs file system.", + }, + "region_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the region.", + }, + "zone_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the zone.", + }, + "zone_name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the zone.", + }, + "bandwidth": { + Type: schema.TypeInt, + Computed: true, + Description: "The bandwidth info of the vepfs file system.", + }, + "version": { + Type: schema.TypeString, + Computed: true, + Description: "The version info of the vepfs file system.", + }, + "account_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the account.", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "The description of the vepfs file system.", + }, + "file_system_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of the vepfs file system.", + }, + "store_type": { + Type: schema.TypeString, + Computed: true, + Description: "The store type of the vepfs file system.", + }, + "store_type_cn": { + Type: schema.TypeString, + Computed: true, + Description: "The store type cn name of the vepfs file system.", + }, + "protocol_type": { + Type: schema.TypeString, + Computed: true, + Description: "The protocol type of the vepfs file system.", + }, + "charge_type": { + Type: schema.TypeString, + Computed: true, + Description: "The charge type of the vepfs file system.", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of the vepfs file system.", + }, + "charge_status": { + Type: schema.TypeString, + Computed: true, + Description: "The charge status of the vepfs file system.", + }, + "project": { + Type: schema.TypeString, + Computed: true, + Description: "The project name of the vepfs file system.", + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "The create time of the vepfs file system.", + }, + "last_modify_time": { + Type: schema.TypeString, + Computed: true, + Description: "The last modify time of the vepfs file system.", + }, + "expire_time": { + Type: schema.TypeString, + Computed: true, + Description: "The expire time of the vepfs file system.", + }, + "stop_service_time": { + Type: schema.TypeString, + Computed: true, + Description: "The stop service time of the vepfs file system.", + }, + "free_time": { + Type: schema.TypeString, + Computed: true, + Description: "The free time of the vepfs file system.", + }, + "capacity_info": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Description: "The capacity info of the vepfs file system.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "total_tib": { + Type: schema.TypeInt, + Computed: true, + Description: "The total size. Unit: TiB.", + }, + "used_gib": { + Type: schema.TypeInt, + Computed: true, + Description: "The used size. Unit: GiB.", + }, + }, + }, + }, + "tags": { + Type: schema.TypeSet, + Computed: true, + Description: "The tags of the vepfs file system.", + Set: vepfsTagsResponseHash, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Computed: true, + Description: "The Key of Tags.", + }, + "value": { + Type: schema.TypeString, + Computed: true, + Description: "The Value of Tags.", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "The Type of Tags.", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func dataSourceVolcengineVepfsFileSystemsRead(d *schema.ResourceData, meta interface{}) error { + service := NewVepfsFileSystemService(meta.(*ve.SdkClient)) + return service.Dispatcher.Data(service, d, DataSourceVolcengineVepfsFileSystems()) +} + +var vepfsTagsResponseHash = func(v interface{}) int { + if v == nil { + return hashcode.String("") + } + m := v.(map[string]interface{}) + var ( + buf bytes.Buffer + ) + buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(m["key"].(string)))) + buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(m["value"].(string)))) + buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(m["type"].(string)))) + return hashcode.String(buf.String()) +} diff --git a/volcengine/vepfs/vepfs_file_system/resource_volcengine_vepfs_file_system.go b/volcengine/vepfs/vepfs_file_system/resource_volcengine_vepfs_file_system.go new file mode 100644 index 0000000..5b839f0 --- /dev/null +++ b/volcengine/vepfs/vepfs_file_system/resource_volcengine_vepfs_file_system.go @@ -0,0 +1,206 @@ +package vepfs_file_system + +import ( + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +/* + +Import +VepfsFileSystem can be imported using the id, e.g. +``` +$ terraform import volcengine_vepfs_file_system.default resource_id +``` + +*/ + +func ResourceVolcengineVepfsFileSystem() *schema.Resource { + resource := &schema.Resource{ + Create: resourceVolcengineVepfsFileSystemCreate, + Read: resourceVolcengineVepfsFileSystemRead, + Update: resourceVolcengineVepfsFileSystemUpdate, + Delete: resourceVolcengineVepfsFileSystemDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "file_system_name": { + Type: schema.TypeString, + Required: true, + Description: "The name of the vepfs file system.", + }, + "subnet_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The subnet id of the vepfs file system.", + }, + "store_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The store type of the vepfs file system. Valid values: `Advance_100`, `Performance` , `Intelligent_Computing`.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "The description info of the vepfs file system.", + }, + "capacity": { + Type: schema.TypeInt, + Required: true, // 实测必须传递 + Description: "The capacity of the vepfs file system.", + }, + "enable_restripe": { + Type: schema.TypeBool, + Optional: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + // 创建时不存在这个参数,修改时存在这个参数 + return d.Id() == "" + }, + Description: "Whether to enable data balance after capacity expansion. This filed is valid only when expanding capacity.", + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + Description: "The project of the vepfs file system.", + }, + "tags": ve.TagsSchema(), + + // computed field + "region_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the region.", + }, + "zone_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the zone.", + }, + "zone_name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the zone.", + }, + "bandwidth": { + Type: schema.TypeInt, + Computed: true, + Description: "The bandwidth info of the vepfs file system.", + }, + "version": { + Type: schema.TypeString, + Computed: true, + Description: "The version info of the vepfs file system.", + }, + "account_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the account.", + }, + "file_system_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of the vepfs file system.", + }, + "store_type_cn": { + Type: schema.TypeString, + Computed: true, + Description: "The store type cn name of the vepfs file system.", + }, + "protocol_type": { + Type: schema.TypeString, + Computed: true, + Description: "The protocol type of the vepfs file system.", + }, + "charge_type": { + Type: schema.TypeString, + Computed: true, + Description: "The charge type of the vepfs file system.", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of the vepfs file system.", + }, + "charge_status": { + Type: schema.TypeString, + Computed: true, + Description: "The charge status of the vepfs file system.", + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "The create time of the vepfs file system.", + }, + "last_modify_time": { + Type: schema.TypeString, + Computed: true, + Description: "The last modify time of the vepfs file system.", + }, + "expire_time": { + Type: schema.TypeString, + Computed: true, + Description: "The expire time of the vepfs file system.", + }, + "stop_service_time": { + Type: schema.TypeString, + Computed: true, + Description: "The stop service time of the vepfs file system.", + }, + "free_time": { + Type: schema.TypeString, + Computed: true, + Description: "The free time of the vepfs file system.", + }, + }, + } + return resource +} + +func resourceVolcengineVepfsFileSystemCreate(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsFileSystemService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Create(service, d, ResourceVolcengineVepfsFileSystem()) + if err != nil { + return fmt.Errorf("error on creating vepfs_file_system %q, %s", d.Id(), err) + } + return resourceVolcengineVepfsFileSystemRead(d, meta) +} + +func resourceVolcengineVepfsFileSystemRead(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsFileSystemService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Read(service, d, ResourceVolcengineVepfsFileSystem()) + if err != nil { + return fmt.Errorf("error on reading vepfs_file_system %q, %s", d.Id(), err) + } + return err +} + +func resourceVolcengineVepfsFileSystemUpdate(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsFileSystemService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Update(service, d, ResourceVolcengineVepfsFileSystem()) + if err != nil { + return fmt.Errorf("error on updating vepfs_file_system %q, %s", d.Id(), err) + } + return resourceVolcengineVepfsFileSystemRead(d, meta) +} + +func resourceVolcengineVepfsFileSystemDelete(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsFileSystemService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Delete(service, d, ResourceVolcengineVepfsFileSystem()) + if err != nil { + return fmt.Errorf("error on deleting vepfs_file_system %q, %s", d.Id(), err) + } + return err +} diff --git a/volcengine/vepfs/vepfs_file_system/service_volcengine_vepfs_file_system.go b/volcengine/vepfs/vepfs_file_system/service_volcengine_vepfs_file_system.go new file mode 100644 index 0000000..f716fe6 --- /dev/null +++ b/volcengine/vepfs/vepfs_file_system/service_volcengine_vepfs_file_system.go @@ -0,0 +1,538 @@ +package vepfs_file_system + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/mitchellh/copystructure" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" + "github.com/volcengine/terraform-provider-volcengine/logger" +) + +type VolcengineVepfsFileSystemService struct { + Client *ve.SdkClient + Dispatcher *ve.Dispatcher +} + +func NewVepfsFileSystemService(c *ve.SdkClient) *VolcengineVepfsFileSystemService { + return &VolcengineVepfsFileSystemService{ + Client: c, + Dispatcher: &ve.Dispatcher{}, + } +} + +func (s *VolcengineVepfsFileSystemService) GetClient() *ve.SdkClient { + return s.Client +} + +func (s *VolcengineVepfsFileSystemService) ReadResources(m map[string]interface{}) (data []interface{}, err error) { + var ( + newCondition map[string]interface{} + resp *map[string]interface{} + results interface{} + ok bool + ) + return ve.WithPageNumberQuery(m, "PageSize", "PageNumber", 100, 1, func(condition map[string]interface{}) ([]interface{}, error) { + action := "DescribeFileSystems" + + deepCopyValue, err := copystructure.Copy(condition) + if err != nil { + return data, fmt.Errorf(" DeepCopy condition error: %v ", err) + } + if newCondition, ok = deepCopyValue.(map[string]interface{}); !ok { + return data, fmt.Errorf(" DeepCopy condition error: newCondition is not map ") + } + + // 处理 FileSystemIds,逗号分离 + if ids, exists := condition["FileSystemIds"]; exists { + idsArr, ok := ids.([]interface{}) + if !ok { + return data, fmt.Errorf(" FileSystemIds is not slice ") + } + fileSystemIds := make([]string, 0) + for _, id := range idsArr { + fileSystemIds = append(fileSystemIds, id.(string)) + } + newCondition["FileSystemIds"] = strings.Join(fileSystemIds, ",") + } + // 处理 Filters + if filters, exists := condition["Filters"]; exists { + filtersMap, ok := filters.(map[string]interface{}) + if !ok { + return data, fmt.Errorf(" Filters is not map ") + } + // 处理 Filters.Status,逗号分离 + if statuses, exists := filtersMap["Status"]; exists { + statusArr, ok := statuses.([]interface{}) + if !ok { + return data, fmt.Errorf(" Filters.Status is not slice ") + } + newStatus := make([]string, 0) + for _, status := range statusArr { + newStatus = append(newStatus, status.(string)) + } + newCondition["Filters"].(map[string]interface{})["Status"] = strings.Join(newStatus, ",") + } + + newFilters := make([]interface{}, 0) + for key, value := range newCondition["Filters"].(map[string]interface{}) { + newFilters = append(newFilters, map[string]interface{}{ + "Key": key, + "Value": value, + }) + } + newCondition["Filters"] = newFilters + } + + bytes, _ := json.Marshal(newCondition) + logger.Debug(logger.ReqFormat, action, string(bytes)) + if newCondition == nil { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), nil) + if err != nil { + return data, err + } + } else { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), &newCondition) + if err != nil { + return data, err + } + } + respBytes, _ := json.Marshal(resp) + logger.Debug(logger.RespFormat, action, newCondition, string(respBytes)) + results, err = ve.ObtainSdkValue("Result.FileSystems", *resp) + if err != nil { + return data, err + } + if results == nil { + results = []interface{}{} + } + if data, ok = results.([]interface{}); !ok { + return data, errors.New("Result.FileSystems is not Slice") + } + return data, err + }) +} + +func (s *VolcengineVepfsFileSystemService) ReadResource(resourceData *schema.ResourceData, id string) (data map[string]interface{}, err error) { + var ( + results []interface{} + ok bool + ) + if id == "" { + id = s.ReadResourceId(resourceData.Id()) + } + + req := map[string]interface{}{ + "FileSystemIds": []interface{}{id}, + } + results, err = s.ReadResources(req) + if err != nil { + return data, err + } + for _, v := range results { + if data, ok = v.(map[string]interface{}); !ok { + return data, errors.New("Value is not map ") + } + } + if len(data) == 0 { + return data, fmt.Errorf("vepfs_file_system %s not exist ", id) + } + + if capacity, exist := data["CapacityInfo"]; exist { + capacityMap, ok := capacity.(map[string]interface{}) + if !ok { + return data, fmt.Errorf(" file system capacity is not map ") + } + data["Capacity"] = capacityMap["TotalTiB"] + } + // 筛选 Custom 标签 + if tags, exist := data["Tags"]; exist { + tagsArr, ok := tags.([]interface{}) + if !ok { + return data, fmt.Errorf(" file system tags is not slice ") + } + newTags := make([]interface{}, 0) + for _, tag := range tagsArr { + tagMap, ok := tag.(map[string]interface{}) + if !ok { + return data, fmt.Errorf(" file system tag is not map") + } + if tagMap["Type"].(string) == "Custom" { + newTags = append(newTags, map[string]interface{}{ + "Key": tagMap["Key"], + "Value": tagMap["Value"], + }) + } + } + data["Tags"] = newTags + } + + return data, err +} + +func (s *VolcengineVepfsFileSystemService) RefreshResourceState(resourceData *schema.ResourceData, target []string, timeout time.Duration, id string) *resource.StateChangeConf { + return &resource.StateChangeConf{ + Pending: []string{}, + Delay: 1 * time.Second, + MinTimeout: 1 * time.Second, + Target: target, + Timeout: timeout, + Refresh: func() (result interface{}, state string, err error) { + var ( + d map[string]interface{} + status interface{} + failStates []string + ) + failStates = append(failStates, "Error") + + // 创建完成后,要等一段时间才能获取到 + if err = resource.Retry(5*time.Minute, func() *resource.RetryError { + d, err = s.ReadResource(resourceData, id) + if err != nil { + if ve.ResourceNotFoundError(err) { + return resource.RetryableError(err) + } else { + return resource.NonRetryableError(err) + } + } + return nil + }); err != nil { + return nil, "", err + } + + status, err = ve.ObtainSdkValue("Status", d) + if err != nil { + return nil, "", err + } + for _, v := range failStates { + if v == status.(string) { + return nil, "", fmt.Errorf("vepfs_file_system status error, status: %s", status.(string)) + } + } + return d, status.(string), err + }, + } +} + +func (VolcengineVepfsFileSystemService) WithResourceResponseHandlers(d map[string]interface{}) []ve.ResourceResponseHandler { + handler := func() (map[string]interface{}, map[string]ve.ResponseConvert, error) { + return d, nil, nil + } + return []ve.ResourceResponseHandler{handler} +} + +func (s *VolcengineVepfsFileSystemService) CreateResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "CreateFileSystem", + ConvertMode: ve.RequestConvertAll, + ContentType: ve.ContentTypeJson, + Convert: map[string]ve.RequestConvert{ + "tags": { + TargetField: "Tags", + ConvertType: ve.ConvertJsonObjectArray, + }, + }, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + subnetId := d.Get("subnet_id") + vpcId, zoneId, err := s.getVpcIdAndZoneIdBySubnet(subnetId.(string)) + if err != nil { + return false, err + } + + (*call.SdkParam)["VpcId"] = vpcId + (*call.SdkParam)["ZoneId"] = zoneId + (*call.SdkParam)["FileSystemType"] = "VePFS" + (*call.SdkParam)["ProtocolType"] = "VePFS" + (*call.SdkParam)["ChargeType"] = "PayAsYouGo" + return true, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + // 将 Tags 的属性赋值为 Custom + if tags, exist := (*call.SdkParam)["Tags"]; exist { + tagsArr, ok := tags.([]interface{}) + if !ok { + return nil, fmt.Errorf(" file system tags is not slice ") + } + for _, tag := range tagsArr { + tagMap, ok := tag.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf(" file system tag is not map") + } + tagMap["Type"] = "Custom" + } + } + + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + id, _ := ve.ObtainSdkValue("Result.FileSystemId", *resp) + d.SetId(id.(string)) + return nil + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Running"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + }, + }, + } + return []ve.Callback{callback} +} + +func (s *VolcengineVepfsFileSystemService) ModifyResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + var callbacks []ve.Callback + + if resourceData.HasChanges("file_system_name", "description", "tags") { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "UpdateFileSystem", + ConvertMode: ve.RequestConvertInConvert, + ContentType: ve.ContentTypeJson, + Convert: map[string]ve.RequestConvert{ + "file_system_name": { + TargetField: "FileSystemName", + }, + "description": { + TargetField: "Description", + }, + "tags": { + TargetField: "Tags", + ForceGet: true, + ConvertType: ve.ConvertJsonObjectArray, + }, + }, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + if len(*call.SdkParam) > 0 { + (*call.SdkParam)["FileSystemId"] = d.Id() + return true, nil + } + return false, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + // 将 Tags 的属性赋值为 Custom + if tags, exists := (*call.SdkParam)["Tags"]; exists { + tagsArr, ok := tags.([]interface{}) + if !ok { + return nil, fmt.Errorf(" file system tags is not slice ") + } + for _, tag := range tagsArr { + tagMap, ok := tag.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf(" file system tag is not map") + } + tagMap["Type"] = "Custom" + } + } else { + // 当 Tags 被删除时,入参添加空列表来置空 + (*call.SdkParam)["Tags"] = []interface{}{} + } + + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Running"}, + Timeout: resourceData.Timeout(schema.TimeoutUpdate), + }, + }, + } + callbacks = append(callbacks, callback) + } + + if resourceData.HasChange("capacity") { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "ExpandFileSystem", + ConvertMode: ve.RequestConvertInConvert, + ContentType: ve.ContentTypeJson, + Convert: map[string]ve.RequestConvert{ + "capacity": { + TargetField: "Capacity", + }, + }, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + if len(*call.SdkParam) > 0 { + enableRestripe := d.Get("enable_restripe") + (*call.SdkParam)["EnableRestripe"] = enableRestripe + (*call.SdkParam)["FileSystemId"] = d.Id() + return true, nil + } + return false, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + // 调用扩容接口之后,要过一段时间才会真正触发扩容操作 + time.Sleep(10 * time.Second) + return nil + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Running"}, + Timeout: resourceData.Timeout(schema.TimeoutUpdate), + }, + }, + } + callbacks = append(callbacks, callback) + } + + return callbacks +} + +func (s *VolcengineVepfsFileSystemService) RemoveResource(resourceData *schema.ResourceData, r *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "DeleteFileSystem", + ConvertMode: ve.RequestConvertIgnore, + ContentType: ve.ContentTypeJson, + SdkParam: &map[string]interface{}{ + "FileSystemId": resourceData.Id(), + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + return s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + return ve.CheckResourceUtilRemoved(d, s.ReadResource, 5*time.Minute) + }, + CallError: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall, baseErr error) error { + //出现错误后重试 + return resource.Retry(15*time.Minute, func() *resource.RetryError { + _, callErr := s.ReadResource(d, "") + if callErr != nil { + if ve.ResourceNotFoundError(callErr) { + return nil + } else { + return resource.NonRetryableError(fmt.Errorf("error on reading vepfs file system on delete %q, %w", d.Id(), callErr)) + } + } + _, callErr = call.ExecuteCall(d, client, call) + if callErr == nil { + return nil + } + return resource.RetryableError(callErr) + }) + }, + }, + } + return []ve.Callback{callback} +} + +func (s *VolcengineVepfsFileSystemService) DatasourceResources(*schema.ResourceData, *schema.Resource) ve.DataSourceInfo { + return ve.DataSourceInfo{ + RequestConverts: map[string]ve.RequestConvert{ + "ids": { + TargetField: "FileSystemIds", + ConvertType: ve.ConvertJsonArray, + }, + "status": { + TargetField: "Filters.Status", + ConvertType: ve.ConvertJsonArray, + }, + "file_system_name": { + TargetField: "Filters.FileSystemName", + }, + "zone_id": { + TargetField: "Filters.ZoneId", + }, + "store_type": { + TargetField: "Filters.StoreType", + }, + "project": { + TargetField: "Project", + }, + }, + NameField: "FileSystemName", + IdField: "FileSystemId", + CollectField: "file_systems", + ContentType: ve.ContentTypeJson, + ResponseConverts: map[string]ve.ResponseConvert{ + "FileSystemId": { + TargetField: "id", + KeepDefault: true, + }, + "TotalTiB": { + TargetField: "total_tib", + }, + "UsedGiB": { + TargetField: "used_gib", + }, + }, + } +} + +func (s *VolcengineVepfsFileSystemService) ReadResourceId(id string) string { + return id +} + +func (s *VolcengineVepfsFileSystemService) getVpcIdAndZoneIdBySubnet(subnetId string) (vpcId, zoneId string, err error) { + // describe subnet + req := map[string]interface{}{ + "SubnetIds.1": subnetId, + } + action := "DescribeSubnets" + resp, err := s.Client.UniversalClient.DoCall(getVpcUniversalInfo(action), &req) + if err != nil { + return "", "", err + } + logger.Debug(logger.RespFormat, action, req, *resp) + results, err := ve.ObtainSdkValue("Result.Subnets", *resp) + if err != nil { + return "", "", err + } + if results == nil { + results = []interface{}{} + } + subnets, ok := results.([]interface{}) + if !ok { + return "", "", errors.New("Result.Subnets is not Slice") + } + if len(subnets) == 0 { + return "", "", fmt.Errorf("subnet %s not exist", subnetId) + } + vpcId = subnets[0].(map[string]interface{})["VpcId"].(string) + zoneId = subnets[0].(map[string]interface{})["ZoneId"].(string) + return vpcId, zoneId, nil +} + +func (s *VolcengineVepfsFileSystemService) ProjectTrn() *ve.ProjectTrn { + return &ve.ProjectTrn{ + ServiceName: "vepfs", + ResourceType: "instance", + ProjectResponseField: "Project", + ProjectSchemaField: "project", + } +} + +func getUniversalInfo(actionName string) ve.UniversalInfo { + return ve.UniversalInfo{ + ServiceName: "vepfs", + Version: "2022-01-01", + HttpMethod: ve.POST, + ContentType: ve.ApplicationJSON, + Action: actionName, + } +} + +func getVpcUniversalInfo(actionName string) ve.UniversalInfo { + return ve.UniversalInfo{ + ServiceName: "vpc", + Version: "2020-04-01", + HttpMethod: ve.GET, + ContentType: ve.Default, + Action: actionName, + } +} diff --git a/volcengine/vepfs/vepfs_fileset/data_source_volcengine_vepfs_filesets.go b/volcengine/vepfs/vepfs_fileset/data_source_volcengine_vepfs_filesets.go new file mode 100644 index 0000000..24f332c --- /dev/null +++ b/volcengine/vepfs/vepfs_fileset/data_source_volcengine_vepfs_filesets.go @@ -0,0 +1,140 @@ +package vepfs_fileset + +import ( + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +func DataSourceVolcengineVepfsFilesets() *schema.Resource { + return &schema.Resource{ + Read: dataSourceVolcengineVepfsFilesetsRead, + Schema: map[string]*schema.Schema{ + "file_system_id": { + Type: schema.TypeString, + Required: true, + Description: "The id of Vepfs File System.", + }, + "fileset_id": { + Type: schema.TypeString, + Optional: true, + Description: "The id of Vepfs Fileset.", + }, + "fileset_name": { + Type: schema.TypeString, + Optional: true, + Description: "The name of Vepfs Fileset. This field support fuzzy query.", + }, + "fileset_path": { + Type: schema.TypeString, + Optional: true, + Description: "The path of Vepfs Fileset. This field support fuzzy query.", + }, + "status": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Set: schema.HashString, + Description: "The query status list of Vepfs Fileset.", + }, + "name_regex": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringIsValidRegExp, + Description: "A Name Regex of Resource.", + }, + "output_file": { + Type: schema.TypeString, + Optional: true, + Description: "File name where to save data source results.", + }, + "total_count": { + Type: schema.TypeInt, + Computed: true, + Description: "The total count of query.", + }, + + "filesets": { + Description: "The collection of query.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the vepfs fileset.", + }, + "fileset_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the vepfs fileset.", + }, + "fileset_name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the vepfs fileset.", + }, + "fileset_path": { + Type: schema.TypeString, + Computed: true, + Description: "The path of the vepfs fileset.", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of the vepfs fileset.", + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "The create time of the vepfs fileset.", + }, + "iops_qos": { + Type: schema.TypeInt, + Computed: true, + Description: "The IOPS Qos of the vepfs fileset.", + }, + "file_used": { + Type: schema.TypeInt, + Computed: true, + Description: "The used file number of the vepfs fileset.", + }, + "file_limit": { + Type: schema.TypeInt, + Computed: true, + Description: "Quota for the number of files or directories. A return of 0 indicates that there is no quota limit set for the number of directories after the file.", + }, + "bandwidth_qos": { + Type: schema.TypeInt, + Computed: true, + Description: "The bandwidth Qos of the vepfs fileset.", + }, + "capacity_used": { + Type: schema.TypeInt, + Computed: true, + Description: "The used capacity of the vepfs fileset. Unit: GiB.", + }, + "capacity_limit": { + Type: schema.TypeInt, + Computed: true, + Description: "The capacity limit of the vepfs fileset. Unit: GiB.", + }, + "max_inode_num": { + Type: schema.TypeInt, + Computed: true, + Description: "The max number of inode in the vepfs fileset.", + }, + }, + }, + }, + }, + } +} + +func dataSourceVolcengineVepfsFilesetsRead(d *schema.ResourceData, meta interface{}) error { + service := NewVepfsFilesetService(meta.(*ve.SdkClient)) + return service.Dispatcher.Data(service, d, DataSourceVolcengineVepfsFilesets()) +} diff --git a/volcengine/vepfs/vepfs_fileset/resource_volcengine_vepfs_fileset.go b/volcengine/vepfs/vepfs_fileset/resource_volcengine_vepfs_fileset.go new file mode 100644 index 0000000..3a32ba9 --- /dev/null +++ b/volcengine/vepfs/vepfs_fileset/resource_volcengine_vepfs_fileset.go @@ -0,0 +1,154 @@ +package vepfs_fileset + +import ( + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +/* + +Import +VepfsFileset can be imported using the file_system_id:fileset_id, e.g. +``` +$ terraform import volcengine_vepfs_fileset.default file_system_id:fileset_id +``` + +*/ + +func ResourceVolcengineVepfsFileset() *schema.Resource { + resource := &schema.Resource{ + Create: resourceVolcengineVepfsFilesetCreate, + Read: resourceVolcengineVepfsFilesetRead, + Update: resourceVolcengineVepfsFilesetUpdate, + Delete: resourceVolcengineVepfsFilesetDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "file_system_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of the vepfs file system.", + }, + "fileset_name": { + Type: schema.TypeString, + Required: true, + Description: "The name of the vepfs fileset.", + }, + "fileset_path": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The path of the vepfs fileset.", + }, + "max_iops": { + Type: schema.TypeInt, + Optional: true, + Description: "The max IOPS qos limit of the vepfs fileset.", + }, + "max_bandwidth": { + Type: schema.TypeInt, + Optional: true, + Description: "The max bandwidth qos limit of the vepfs fileset. Unit: MB/s.", + }, + "file_limit": { + Type: schema.TypeInt, + Optional: true, + Description: "The file number limit of the vepfs fileset.", + }, + "capacity_limit": { + Type: schema.TypeInt, + Optional: true, + Description: "The capacity limit of the vepfs fileset. Unit: Gib.", + }, + + // computed fields + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of the vepfs fileset.", + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "The create time of the vepfs fileset.", + }, + "file_used": { + Type: schema.TypeInt, + Computed: true, + Description: "The used file number of the vepfs fileset.", + }, + "capacity_used": { + Type: schema.TypeInt, + Computed: true, + Description: "The used capacity of the vepfs fileset. Unit: GiB.", + }, + "max_inode_num": { + Type: schema.TypeInt, + Computed: true, + Description: "The max number of inode in the vepfs fileset.", + }, + }, + } + return resource +} + +func resourceVolcengineVepfsFilesetCreate(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsFilesetService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Create(service, d, ResourceVolcengineVepfsFileset()) + if err != nil { + return fmt.Errorf("error on creating vepfs_fileset %q, %s", d.Id(), err) + } + return resourceVolcengineVepfsFilesetRead(d, meta) +} + +func resourceVolcengineVepfsFilesetRead(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsFilesetService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Read(service, d, ResourceVolcengineVepfsFileset()) + if err != nil { + return fmt.Errorf("error on reading vepfs_fileset %q, %s", d.Id(), err) + } + return err +} + +func resourceVolcengineVepfsFilesetUpdate(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsFilesetService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Update(service, d, ResourceVolcengineVepfsFileset()) + if err != nil { + return fmt.Errorf("error on updating vepfs_fileset %q, %s", d.Id(), err) + } + return resourceVolcengineVepfsFilesetRead(d, meta) +} + +func resourceVolcengineVepfsFilesetDelete(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsFilesetService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Delete(service, d, ResourceVolcengineVepfsFileset()) + if err != nil { + return fmt.Errorf("error on deleting vepfs_fileset %q, %s", d.Id(), err) + } + return err +} + +var filesetImporter = func(data *schema.ResourceData, i interface{}) ([]*schema.ResourceData, error) { + items := strings.Split(data.Id(), ":") + if len(items) != 2 { + return []*schema.ResourceData{data}, fmt.Errorf("import id must split with ':'") + } + if err := data.Set("file_system_id", items[0]); err != nil { + return []*schema.ResourceData{data}, err + } + if err := data.Set("fileset_id", items[1]); err != nil { + return []*schema.ResourceData{data}, err + } + return []*schema.ResourceData{data}, nil +} diff --git a/volcengine/vepfs/vepfs_fileset/service_volcengine_vepfs_fileset.go b/volcengine/vepfs/vepfs_fileset/service_volcengine_vepfs_fileset.go new file mode 100644 index 0000000..a4c31b1 --- /dev/null +++ b/volcengine/vepfs/vepfs_fileset/service_volcengine_vepfs_fileset.go @@ -0,0 +1,539 @@ +package vepfs_fileset + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/mitchellh/copystructure" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" + "github.com/volcengine/terraform-provider-volcengine/logger" +) + +type VolcengineVepfsFilesetService struct { + Client *ve.SdkClient + Dispatcher *ve.Dispatcher +} + +func NewVepfsFilesetService(c *ve.SdkClient) *VolcengineVepfsFilesetService { + return &VolcengineVepfsFilesetService{ + Client: c, + Dispatcher: &ve.Dispatcher{}, + } +} + +func (s *VolcengineVepfsFilesetService) GetClient() *ve.SdkClient { + return s.Client +} + +func (s *VolcengineVepfsFilesetService) ReadResources(m map[string]interface{}) (data []interface{}, err error) { + var ( + newCondition map[string]interface{} + resp *map[string]interface{} + results interface{} + ok bool + ) + return ve.WithPageNumberQuery(m, "PageSize", "PageNumber", 100, 1, func(condition map[string]interface{}) ([]interface{}, error) { + action := "DescribeFilesets" + + deepCopyValue, err := copystructure.Copy(condition) + if err != nil { + return data, fmt.Errorf(" DeepCopy condition error: %v ", err) + } + if newCondition, ok = deepCopyValue.(map[string]interface{}); !ok { + return data, fmt.Errorf(" DeepCopy condition error: newCondition is not map ") + } + + // 处理 Filters + if filters, exists := condition["Filters"]; exists { + filtersMap, ok := filters.(map[string]interface{}) + if !ok { + return data, fmt.Errorf(" Filters is not map ") + } + // 处理 Filters.Status,逗号分离 + if statuses, exists := filtersMap["Status"]; exists { + statusArr, ok := statuses.([]interface{}) + if !ok { + return data, fmt.Errorf(" Filters.Status is not slice ") + } + newStatus := make([]string, 0) + for _, status := range statusArr { + newStatus = append(newStatus, status.(string)) + } + newCondition["Filters"].(map[string]interface{})["Status"] = strings.Join(newStatus, ",") + } + + newFilters := make([]interface{}, 0) + for key, value := range newCondition["Filters"].(map[string]interface{}) { + newFilters = append(newFilters, map[string]interface{}{ + "Key": key, + "Value": value, + }) + } + newCondition["Filters"] = newFilters + } + + bytes, _ := json.Marshal(newCondition) + logger.Debug(logger.ReqFormat, action, string(bytes)) + if newCondition == nil { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), nil) + if err != nil { + return data, err + } + } else { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), &newCondition) + if err != nil { + return data, err + } + } + respBytes, _ := json.Marshal(resp) + logger.Debug(logger.RespFormat, action, newCondition, string(respBytes)) + results, err = ve.ObtainSdkValue("Result.Filesets", *resp) + if err != nil { + return data, err + } + if results == nil { + results = []interface{}{} + } + if data, ok = results.([]interface{}); !ok { + return data, errors.New("Result.Filesets is not Slice") + } + return data, err + }) +} + +func (s *VolcengineVepfsFilesetService) ReadResource(resourceData *schema.ResourceData, id string) (data map[string]interface{}, err error) { + var ( + results []interface{} + ok bool + ) + if id == "" { + id = s.ReadResourceId(resourceData.Id()) + } + ids := strings.Split(id, ":") + if len(ids) != 2 { + return data, fmt.Errorf("invalid vepfs fileset Id: %s", id) + } + + req := map[string]interface{}{ + "FileSystemId": ids[0], + "Filters": map[string]interface{}{ + "FilesetId": ids[1], + }, + } + results, err = s.ReadResources(req) + if err != nil { + return data, err + } + for _, v := range results { + if data, ok = v.(map[string]interface{}); !ok { + return data, errors.New("Value is not map ") + } + } + if len(data) == 0 { + return data, fmt.Errorf("vepfs_fileset %s not exist ", id) + } + + // 特殊处理 FilesetPath + if filePath, exist := data["FilesetPath"]; exist { + newPath := filePath.(string) + "/" + data["FilesetPath"] = newPath + } + + return data, err +} + +func (s *VolcengineVepfsFilesetService) RefreshResourceState(resourceData *schema.ResourceData, target []string, timeout time.Duration, id string) *resource.StateChangeConf { + return &resource.StateChangeConf{ + Pending: []string{}, + Delay: 1 * time.Second, + MinTimeout: 1 * time.Second, + Target: target, + Timeout: timeout, + Refresh: func() (result interface{}, state string, err error) { + var ( + d map[string]interface{} + status interface{} + failStates []string + ) + failStates = append(failStates, "Error") + failStates = append(failStates, "CreateError") + failStates = append(failStates, "UpdateError") + failStates = append(failStates, "DeleteError") + d, err = s.ReadResource(resourceData, id) + if err != nil { + return nil, "", err + } + status, err = ve.ObtainSdkValue("Status", d) + if err != nil { + return nil, "", err + } + for _, v := range failStates { + if v == status.(string) { + return nil, "", fmt.Errorf("vepfs_fileset status error, status: %s", status.(string)) + } + } + return d, status.(string), err + }, + } +} + +func (VolcengineVepfsFilesetService) WithResourceResponseHandlers(d map[string]interface{}) []ve.ResourceResponseHandler { + handler := func() (map[string]interface{}, map[string]ve.ResponseConvert, error) { + return d, map[string]ve.ResponseConvert{ + "IOPSQos": { + TargetField: "max_iops", + }, + "BandwidthQos": { + TargetField: "max_bandwidth", + }, + }, nil + } + return []ve.ResourceResponseHandler{handler} +} + +func (s *VolcengineVepfsFilesetService) CreateResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + var callbacks []ve.Callback + + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "CreateFileset", + ConvertMode: ve.RequestConvertInConvert, + ContentType: ve.ContentTypeJson, + Convert: map[string]ve.RequestConvert{ + "file_system_id": { + TargetField: "FileSystemId", + }, + "fileset_name": { + TargetField: "FilesetName", + }, + "fileset_path": { + TargetField: "FilesetPath", + }, + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + filesetId, _ := ve.ObtainSdkValue("Result.FilesetId", *resp) + fileSystemId := d.Get("file_system_id") + d.SetId(fileSystemId.(string) + ":" + filesetId.(string)) + return nil + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Running"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + }, + }, + } + callbacks = append(callbacks, callback) + + // 设置 Qos + _, ok1 := resourceData.GetOk("max_iops") + _, ok2 := resourceData.GetOk("max_bandwidth") + if ok1 || ok2 { + qosCallback := ve.Callback{ + Call: ve.SdkCall{ + Action: "SetFilesetQos", + ConvertMode: ve.RequestConvertInConvert, + ContentType: ve.ContentTypeJson, + Convert: map[string]ve.RequestConvert{ + "max_iops": { + TargetField: "MaxIops", + }, + "max_bandwidth": { + TargetField: "MaxBandwidth", + }, + }, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + ids := strings.Split(d.Id(), ":") + if len(ids) != 2 { + return false, fmt.Errorf("invalid vepfs fileset Id: %s", d.Id()) + } + + (*call.SdkParam)["FileSystemId"] = ids[0] + (*call.SdkParam)["FilesetId"] = ids[1] + return true, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Running"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + }, + }, + } + callbacks = append(callbacks, qosCallback) + } + + // 设置 Quota + _, ok3 := resourceData.GetOk("file_limit") + _, ok4 := resourceData.GetOk("capacity_limit") + if ok3 || ok4 { + quotaCallback := ve.Callback{ + Call: ve.SdkCall{ + Action: "SetFilesetQuota", + ConvertMode: ve.RequestConvertInConvert, + ContentType: ve.ContentTypeJson, + Convert: map[string]ve.RequestConvert{ + "file_limit": { + TargetField: "FileLimit", + }, + "capacity_limit": { + TargetField: "CapacityLimit", + }, + }, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + ids := strings.Split(d.Id(), ":") + if len(ids) != 2 { + return false, fmt.Errorf("invalid vepfs fileset Id: %s", d.Id()) + } + + (*call.SdkParam)["FileSystemId"] = ids[0] + (*call.SdkParam)["FilesetId"] = ids[1] + return true, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Running"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + }, + }, + } + callbacks = append(callbacks, quotaCallback) + } + + return callbacks +} + +func (s *VolcengineVepfsFilesetService) ModifyResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + var callbacks []ve.Callback + + if resourceData.HasChange("fileset_name") { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "UpdateFileset", + ConvertMode: ve.RequestConvertInConvert, + ContentType: ve.ContentTypeJson, + Convert: map[string]ve.RequestConvert{ + "fileset_name": { + TargetField: "FilesetName", + }, + }, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + ids := strings.Split(d.Id(), ":") + if len(ids) != 2 { + return false, fmt.Errorf("invalid vepfs fileset Id: %s", d.Id()) + } + + (*call.SdkParam)["FileSystemId"] = ids[0] + (*call.SdkParam)["FilesetId"] = ids[1] + return true, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Running"}, + Timeout: resourceData.Timeout(schema.TimeoutUpdate), + }, + }, + } + callbacks = append(callbacks, callback) + } + + if resourceData.HasChanges("max_iops", "max_bandwidth") { + qosCallback := ve.Callback{ + Call: ve.SdkCall{ + Action: "SetFilesetQos", + ConvertMode: ve.RequestConvertInConvert, + ContentType: ve.ContentTypeJson, + Convert: map[string]ve.RequestConvert{ + "max_iops": { + TargetField: "MaxIops", + }, + "max_bandwidth": { + TargetField: "MaxBandwidth", + }, + }, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + ids := strings.Split(d.Id(), ":") + if len(ids) != 2 { + return false, fmt.Errorf("invalid vepfs fileset Id: %s", d.Id()) + } + + (*call.SdkParam)["FileSystemId"] = ids[0] + (*call.SdkParam)["FilesetId"] = ids[1] + return true, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Running"}, + Timeout: resourceData.Timeout(schema.TimeoutUpdate), + }, + }, + } + callbacks = append(callbacks, qosCallback) + } + + if resourceData.HasChanges("file_limit", "capacity_limit") { + quotaCallback := ve.Callback{ + Call: ve.SdkCall{ + Action: "SetFilesetQuota", + ConvertMode: ve.RequestConvertInConvert, + ContentType: ve.ContentTypeJson, + Convert: map[string]ve.RequestConvert{ + "file_limit": { + TargetField: "FileLimit", + }, + "capacity_limit": { + TargetField: "CapacityLimit", + }, + }, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + ids := strings.Split(d.Id(), ":") + if len(ids) != 2 { + return false, fmt.Errorf("invalid vepfs fileset Id: %s", d.Id()) + } + + (*call.SdkParam)["FileSystemId"] = ids[0] + (*call.SdkParam)["FilesetId"] = ids[1] + return true, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Running"}, + Timeout: resourceData.Timeout(schema.TimeoutUpdate), + }, + }, + } + callbacks = append(callbacks, quotaCallback) + } + + return callbacks +} + +func (s *VolcengineVepfsFilesetService) RemoveResource(resourceData *schema.ResourceData, r *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "DeleteFileset", + ConvertMode: ve.RequestConvertIgnore, + ContentType: ve.ContentTypeJson, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + ids := strings.Split(d.Id(), ":") + if len(ids) != 2 { + return false, fmt.Errorf("invalid vepfs fileset Id: %s", d.Id()) + } + + (*call.SdkParam)["FileSystemId"] = ids[0] + (*call.SdkParam)["FilesetId"] = ids[1] + return true, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + return s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + return ve.CheckResourceUtilRemoved(d, s.ReadResource, 5*time.Minute) + }, + CallError: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall, baseErr error) error { + //出现错误后重试 + return resource.Retry(15*time.Minute, func() *resource.RetryError { + _, callErr := s.ReadResource(d, "") + if callErr != nil { + if ve.ResourceNotFoundError(callErr) { + return nil + } else { + return resource.NonRetryableError(fmt.Errorf("error on reading vepfs fileset on delete %q, %w", d.Id(), callErr)) + } + } + _, callErr = call.ExecuteCall(d, client, call) + if callErr == nil { + return nil + } + return resource.RetryableError(callErr) + }) + }, + }, + } + return []ve.Callback{callback} +} + +func (s *VolcengineVepfsFilesetService) DatasourceResources(*schema.ResourceData, *schema.Resource) ve.DataSourceInfo { + return ve.DataSourceInfo{ + RequestConverts: map[string]ve.RequestConvert{ + "status": { + TargetField: "Filters.Status", + ConvertType: ve.ConvertJsonArray, + }, + "file_system_id": { + TargetField: "FileSystemId", + }, + "fileset_id": { + TargetField: "Filters.FilesetId", + }, + "fileset_name": { + TargetField: "Filters.FilesetName", + }, + "fileset_path": { + TargetField: "Filters.FilesetPath", + }, + }, + NameField: "FilesetName", + IdField: "FilesetId", + CollectField: "filesets", + ContentType: ve.ContentTypeJson, + ResponseConverts: map[string]ve.ResponseConvert{ + "FilesetId": { + TargetField: "id", + KeepDefault: true, + }, + "IOPSQos": { + TargetField: "iops_qos", + }, + }, + } +} + +func (s *VolcengineVepfsFilesetService) ReadResourceId(id string) string { + return id +} + +func getUniversalInfo(actionName string) ve.UniversalInfo { + return ve.UniversalInfo{ + ServiceName: "vepfs", + Version: "2022-01-01", + HttpMethod: ve.POST, + ContentType: ve.ApplicationJSON, + Action: actionName, + } +} diff --git a/volcengine/vepfs/vepfs_mount_service/data_source_volcengine_vepfs_mount_services.go b/volcengine/vepfs/vepfs_mount_service/data_source_volcengine_vepfs_mount_services.go new file mode 100644 index 0000000..b18963b --- /dev/null +++ b/volcengine/vepfs/vepfs_mount_service/data_source_volcengine_vepfs_mount_services.go @@ -0,0 +1,184 @@ +package vepfs_mount_service + +import ( + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +func DataSourceVolcengineVepfsMountServices() *schema.Resource { + return &schema.Resource{ + Read: dataSourceVolcengineVepfsMountServicesRead, + Schema: map[string]*schema.Schema{ + "file_system_id": { + Type: schema.TypeString, + Optional: true, + Description: "The id of Vepfs File System.", + }, + "mount_service_id": { + Type: schema.TypeString, + Optional: true, + Description: "The id of mount service.", + }, + "mount_service_name": { + Type: schema.TypeString, + Optional: true, + Description: "The name of mount service. This field support fuzzy query.", + }, + "status": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Set: schema.HashString, + Description: "The query status list of mount service.", + }, + "name_regex": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringIsValidRegExp, + Description: "A Name Regex of Resource.", + }, + "output_file": { + Type: schema.TypeString, + Optional: true, + Description: "File name where to save data source results.", + }, + "total_count": { + Type: schema.TypeInt, + Computed: true, + Description: "The total count of query.", + }, + + "mount_services": { + Description: "The collection of query.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the mount service.", + }, + "mount_service_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the mount service.", + }, + "mount_service_name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the mount service.", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of the mount service.", + }, + "project": { + Type: schema.TypeString, + Computed: true, + Description: "The project of the mount service.", + }, + "account_id": { + Type: schema.TypeString, + Computed: true, + Description: "The account id of the mount service.", + }, + "region_id": { + Type: schema.TypeString, + Computed: true, + Description: "The region id of the mount service.", + }, + "zone_id": { + Type: schema.TypeString, + Computed: true, + Description: "The zone id of the mount service.", + }, + "zone_name": { + Type: schema.TypeString, + Computed: true, + Description: "The zone name of the mount service.", + }, + "vpc_id": { + Type: schema.TypeString, + Computed: true, + Description: "The vpc id of the mount service.", + }, + "subnet_id": { + Type: schema.TypeString, + Computed: true, + Description: "The subnet id of the mount service.", + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "The created time of the mount service.", + }, + "nodes": { + Type: schema.TypeList, + Computed: true, + Description: "The nodes info of the mount service.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "node_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of ecs instance.", + }, + "default_password": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + Description: "The default password of ecs instance.", + }, + }, + }, + }, + "attach_file_systems": { + Type: schema.TypeList, + Computed: true, + Description: "The attached file system info of the mount service.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "file_system_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the vepfs file system.", + }, + "file_system_name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the vepfs file system.", + }, + "account_id": { + Type: schema.TypeString, + Computed: true, + Description: "The account id of the vepfs file system.", + }, + "customer_path": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the vepfs file system.", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of the vepfs file system.", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func dataSourceVolcengineVepfsMountServicesRead(d *schema.ResourceData, meta interface{}) error { + service := NewVepfsMountServiceService(meta.(*ve.SdkClient)) + return service.Dispatcher.Data(service, d, DataSourceVolcengineVepfsMountServices()) +} diff --git a/volcengine/vepfs/vepfs_mount_service/resource_volcengine_vepfs_mount_service.go b/volcengine/vepfs/vepfs_mount_service/resource_volcengine_vepfs_mount_service.go new file mode 100644 index 0000000..933474e --- /dev/null +++ b/volcengine/vepfs/vepfs_mount_service/resource_volcengine_vepfs_mount_service.go @@ -0,0 +1,190 @@ +package vepfs_mount_service + +import ( + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +/* + +Import +VepfsMountService can be imported using the id, e.g. +``` +$ terraform import volcengine_vepfs_mount_service.default resource_id +``` + +*/ + +func ResourceVolcengineVepfsMountService() *schema.Resource { + resource := &schema.Resource{ + Create: resourceVolcengineVepfsMountServiceCreate, + Read: resourceVolcengineVepfsMountServiceRead, + Update: resourceVolcengineVepfsMountServiceUpdate, + Delete: resourceVolcengineVepfsMountServiceDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "mount_service_name": { + Type: schema.TypeString, + Required: true, + Description: "The name of the mount service.", + }, + "subnet_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The subnet id of the mount service.", + }, + "node_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The node type of the mount service. When importing resources, this attribute will not be imported. If this attribute is set, please use lifecycle and ignore_changes ignore changes in fields.", + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + Description: "The node type of the mount service.", + }, + + // computed fields + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of the mount service.", + }, + "account_id": { + Type: schema.TypeString, + Computed: true, + Description: "The account id of the mount service.", + }, + "region_id": { + Type: schema.TypeString, + Computed: true, + Description: "The region id of the mount service.", + }, + "zone_id": { + Type: schema.TypeString, + Computed: true, + Description: "The zone id of the mount service.", + }, + "zone_name": { + Type: schema.TypeString, + Computed: true, + Description: "The zone name of the mount service.", + }, + "vpc_id": { + Type: schema.TypeString, + Computed: true, + Description: "The vpc id of the mount service.", + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "The created time of the mount service.", + }, + "nodes": { + Type: schema.TypeList, + Computed: true, + Description: "The nodes info of the mount service.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "node_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of ecs instance.", + }, + "default_password": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + Description: "The default password of ecs instance.", + }, + }, + }, + }, + "attach_file_systems": { + Type: schema.TypeList, + Computed: true, + Description: "The attached file system info of the mount service.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "file_system_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the vepfs file system.", + }, + "file_system_name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the vepfs file system.", + }, + "account_id": { + Type: schema.TypeString, + Computed: true, + Description: "The account id of the vepfs file system.", + }, + "customer_path": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the vepfs file system.", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of the vepfs file system.", + }, + }, + }, + }, + }, + } + return resource +} + +func resourceVolcengineVepfsMountServiceCreate(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsMountServiceService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Create(service, d, ResourceVolcengineVepfsMountService()) + if err != nil { + return fmt.Errorf("error on creating vepfs_mount_service %q, %s", d.Id(), err) + } + return resourceVolcengineVepfsMountServiceRead(d, meta) +} + +func resourceVolcengineVepfsMountServiceRead(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsMountServiceService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Read(service, d, ResourceVolcengineVepfsMountService()) + if err != nil { + return fmt.Errorf("error on reading vepfs_mount_service %q, %s", d.Id(), err) + } + return err +} + +func resourceVolcengineVepfsMountServiceUpdate(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsMountServiceService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Update(service, d, ResourceVolcengineVepfsMountService()) + if err != nil { + return fmt.Errorf("error on updating vepfs_mount_service %q, %s", d.Id(), err) + } + return resourceVolcengineVepfsMountServiceRead(d, meta) +} + +func resourceVolcengineVepfsMountServiceDelete(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsMountServiceService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Delete(service, d, ResourceVolcengineVepfsMountService()) + if err != nil { + return fmt.Errorf("error on deleting vepfs_mount_service %q, %s", d.Id(), err) + } + return err +} diff --git a/volcengine/vepfs/vepfs_mount_service/service_volcengine_vepfs_mount_service.go b/volcengine/vepfs/vepfs_mount_service/service_volcengine_vepfs_mount_service.go new file mode 100644 index 0000000..65ca779 --- /dev/null +++ b/volcengine/vepfs/vepfs_mount_service/service_volcengine_vepfs_mount_service.go @@ -0,0 +1,383 @@ +package vepfs_mount_service + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/mitchellh/copystructure" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" + "github.com/volcengine/terraform-provider-volcengine/logger" +) + +type VolcengineVepfsMountServiceService struct { + Client *ve.SdkClient + Dispatcher *ve.Dispatcher +} + +func NewVepfsMountServiceService(c *ve.SdkClient) *VolcengineVepfsMountServiceService { + return &VolcengineVepfsMountServiceService{ + Client: c, + Dispatcher: &ve.Dispatcher{}, + } +} + +func (s *VolcengineVepfsMountServiceService) GetClient() *ve.SdkClient { + return s.Client +} + +func (s *VolcengineVepfsMountServiceService) ReadResources(m map[string]interface{}) (data []interface{}, err error) { + var ( + newCondition map[string]interface{} + resp *map[string]interface{} + results interface{} + ok bool + ) + return ve.WithPageNumberQuery(m, "PageSize", "PageNumber", 100, 1, func(condition map[string]interface{}) ([]interface{}, error) { + action := "DescribeMountServices" + + deepCopyValue, err := copystructure.Copy(condition) + if err != nil { + return data, fmt.Errorf(" DeepCopy condition error: %v ", err) + } + if newCondition, ok = deepCopyValue.(map[string]interface{}); !ok { + return data, fmt.Errorf(" DeepCopy condition error: newCondition is not map ") + } + + // 处理 Filters + if filters, exists := condition["Filters"]; exists { + filtersMap, ok := filters.(map[string]interface{}) + if !ok { + return data, fmt.Errorf(" Filters is not map ") + } + // 处理 Filters.Status,逗号分离 + if statuses, exists := filtersMap["Status"]; exists { + statusArr, ok := statuses.([]interface{}) + if !ok { + return data, fmt.Errorf(" Filters.Status is not slice ") + } + newStatus := make([]string, 0) + for _, status := range statusArr { + newStatus = append(newStatus, status.(string)) + } + newCondition["Filters"].(map[string]interface{})["Status"] = strings.Join(newStatus, ",") + } + + newFilters := make([]interface{}, 0) + for key, value := range newCondition["Filters"].(map[string]interface{}) { + newFilters = append(newFilters, map[string]interface{}{ + "Key": key, + "Value": value, + }) + } + newCondition["Filters"] = newFilters + } + + bytes, _ := json.Marshal(newCondition) + logger.Debug(logger.ReqFormat, action, string(bytes)) + if newCondition == nil { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), nil) + if err != nil { + return data, err + } + } else { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), &newCondition) + if err != nil { + return data, err + } + } + respBytes, _ := json.Marshal(resp) + logger.Debug(logger.RespFormat, action, newCondition, string(respBytes)) + results, err = ve.ObtainSdkValue("Result.MountServices", *resp) + if err != nil { + return data, err + } + if results == nil { + results = []interface{}{} + } + if data, ok = results.([]interface{}); !ok { + return data, errors.New("Result.MountServices is not Slice") + } + return data, err + }) +} + +func (s *VolcengineVepfsMountServiceService) ReadResource(resourceData *schema.ResourceData, id string) (data map[string]interface{}, err error) { + var ( + results []interface{} + ok bool + ) + if id == "" { + id = s.ReadResourceId(resourceData.Id()) + } + + req := map[string]interface{}{ + "Filters": map[string]interface{}{ + "MountServiceId": id, + }, + } + results, err = s.ReadResources(req) + if err != nil { + return data, err + } + for _, v := range results { + if data, ok = v.(map[string]interface{}); !ok { + return data, errors.New("Value is not map ") + } + } + if len(data) == 0 { + return data, fmt.Errorf("vepfs_mount_service %s not exist ", id) + } + + if _, exist := data["AttachFileSystems"]; !exist { + data["AttachFileSystems"] = []interface{}{} + } + + return data, err +} + +func (s *VolcengineVepfsMountServiceService) RefreshResourceState(resourceData *schema.ResourceData, target []string, timeout time.Duration, id string) *resource.StateChangeConf { + return &resource.StateChangeConf{ + Pending: []string{}, + Delay: 1 * time.Second, + MinTimeout: 1 * time.Second, + Target: target, + Timeout: timeout, + Refresh: func() (result interface{}, state string, err error) { + var ( + d map[string]interface{} + status interface{} + failStates []string + ) + failStates = append(failStates, "Error") + d, err = s.ReadResource(resourceData, id) + if err != nil { + return nil, "", err + } + status, err = ve.ObtainSdkValue("Status", d) + if err != nil { + return nil, "", err + } + for _, v := range failStates { + if v == status.(string) { + return nil, "", fmt.Errorf("vepfs_mount_service status error, status: %s", status.(string)) + } + } + return d, status.(string), err + }, + } +} + +func (VolcengineVepfsMountServiceService) WithResourceResponseHandlers(d map[string]interface{}) []ve.ResourceResponseHandler { + handler := func() (map[string]interface{}, map[string]ve.ResponseConvert, error) { + return d, nil, nil + } + return []ve.ResourceResponseHandler{handler} +} + +func (s *VolcengineVepfsMountServiceService) CreateResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "CreateMountService", + ConvertMode: ve.RequestConvertAll, + ContentType: ve.ContentTypeJson, + Convert: map[string]ve.RequestConvert{}, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + subnetId := d.Get("subnet_id") + vpcId, zoneId, err := s.getVpcIdAndZoneIdBySubnet(subnetId.(string)) + if err != nil { + return false, err + } + + (*call.SdkParam)["VpcId"] = vpcId + (*call.SdkParam)["ZoneId"] = zoneId + return true, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + id, _ := ve.ObtainSdkValue("Result.MountServiceId", *resp) + d.SetId(id.(string)) + return nil + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Running"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + }, + }, + } + return []ve.Callback{callback} +} + +func (s *VolcengineVepfsMountServiceService) ModifyResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "UpdateMountService", + ConvertMode: ve.RequestConvertInConvert, + ContentType: ve.ContentTypeJson, + Convert: map[string]ve.RequestConvert{ + "mount_service_name": { + TargetField: "MountServiceName", + }, + }, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + if len(*call.SdkParam) > 0 { + (*call.SdkParam)["MountServiceId"] = d.Id() + return true, nil + } + return false, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.ReqFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Running"}, + Timeout: resourceData.Timeout(schema.TimeoutUpdate), + }, + }, + } + return []ve.Callback{callback} +} + +func (s *VolcengineVepfsMountServiceService) RemoveResource(resourceData *schema.ResourceData, r *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "DeleteMountService", + ConvertMode: ve.RequestConvertIgnore, + ContentType: ve.ContentTypeJson, + SdkParam: &map[string]interface{}{ + "MountServiceId": resourceData.Id(), + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + return s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + return ve.CheckResourceUtilRemoved(d, s.ReadResource, 5*time.Minute) + }, + CallError: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall, baseErr error) error { + //出现错误后重试 + return resource.Retry(15*time.Minute, func() *resource.RetryError { + _, callErr := s.ReadResource(d, "") + if callErr != nil { + if ve.ResourceNotFoundError(callErr) { + return nil + } else { + return resource.NonRetryableError(fmt.Errorf("error on reading vepfs mount service on delete %q, %w", d.Id(), callErr)) + } + } + _, callErr = call.ExecuteCall(d, client, call) + if callErr == nil { + return nil + } + return resource.RetryableError(callErr) + }) + }, + }, + } + return []ve.Callback{callback} +} + +func (s *VolcengineVepfsMountServiceService) DatasourceResources(*schema.ResourceData, *schema.Resource) ve.DataSourceInfo { + return ve.DataSourceInfo{ + RequestConverts: map[string]ve.RequestConvert{ + "status": { + TargetField: "Filters.Status", + ConvertType: ve.ConvertJsonArray, + }, + "file_system_id": { + TargetField: "Filters.FileSystemId", + }, + "mount_service_id": { + TargetField: "Filters.MountServiceId", + }, + "mount_service_name": { + TargetField: "Filters.MountServiceName", + }, + }, + NameField: "MountServiceName", + IdField: "MountServiceId", + CollectField: "mount_services", + ContentType: ve.ContentTypeJson, + ResponseConverts: map[string]ve.ResponseConvert{ + "MountServiceId": { + TargetField: "id", + KeepDefault: true, + }, + }, + } +} + +func (s *VolcengineVepfsMountServiceService) ReadResourceId(id string) string { + return id +} + +func (s *VolcengineVepfsMountServiceService) getVpcIdAndZoneIdBySubnet(subnetId string) (vpcId, zoneId string, err error) { + // describe subnet + req := map[string]interface{}{ + "SubnetIds.1": subnetId, + } + action := "DescribeSubnets" + resp, err := s.Client.UniversalClient.DoCall(getVpcUniversalInfo(action), &req) + if err != nil { + return "", "", err + } + logger.Debug(logger.RespFormat, action, req, *resp) + results, err := ve.ObtainSdkValue("Result.Subnets", *resp) + if err != nil { + return "", "", err + } + if results == nil { + results = []interface{}{} + } + subnets, ok := results.([]interface{}) + if !ok { + return "", "", errors.New("Result.Subnets is not Slice") + } + if len(subnets) == 0 { + return "", "", fmt.Errorf("subnet %s not exist", subnetId) + } + vpcId = subnets[0].(map[string]interface{})["VpcId"].(string) + zoneId = subnets[0].(map[string]interface{})["ZoneId"].(string) + return vpcId, zoneId, nil +} + +func (s *VolcengineVepfsMountServiceService) ProjectTrn() *ve.ProjectTrn { + return &ve.ProjectTrn{ + ServiceName: "vepfs", + ResourceType: "mountservice", + ProjectResponseField: "Project", + ProjectSchemaField: "project", + } +} + +func getUniversalInfo(actionName string) ve.UniversalInfo { + return ve.UniversalInfo{ + ServiceName: "vepfs", + Version: "2022-01-01", + HttpMethod: ve.POST, + ContentType: ve.ApplicationJSON, + Action: actionName, + } +} + +func getVpcUniversalInfo(actionName string) ve.UniversalInfo { + return ve.UniversalInfo{ + ServiceName: "vpc", + Version: "2020-04-01", + HttpMethod: ve.GET, + ContentType: ve.Default, + Action: actionName, + } +} diff --git a/volcengine/vepfs/vepfs_mount_service_attachment/resource_volcengine_vepfs_mount_service_attachment.go b/volcengine/vepfs/vepfs_mount_service_attachment/resource_volcengine_vepfs_mount_service_attachment.go new file mode 100644 index 0000000..402156b --- /dev/null +++ b/volcengine/vepfs/vepfs_mount_service_attachment/resource_volcengine_vepfs_mount_service_attachment.go @@ -0,0 +1,114 @@ +package vepfs_mount_service_attachment + +import ( + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" +) + +/* + +Import +VepfsMountServiceAttachment can be imported using the mount_service_id:file_system_id, e.g. +``` +$ terraform import volcengine_vepfs_mount_service_attachment.default mount_service_id:file_system_id +``` + +*/ + +func ResourceVolcengineVepfsMountServiceAttachment() *schema.Resource { + resource := &schema.Resource{ + Create: resourceVolcengineVepfsMountServiceAttachmentCreate, + Read: resourceVolcengineVepfsMountServiceAttachmentRead, + Delete: resourceVolcengineVepfsMountServiceAttachmentDelete, + Importer: &schema.ResourceImporter{ + State: attachmentImporter, + }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "mount_service_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of the mount service.", + }, + "file_system_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of the vepfs file system.", + }, + "customer_path": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + Description: "The custom mount directory, the default value is file system id.", + }, + + // computed fields + "attach_status": { + Type: schema.TypeString, + Computed: true, + Description: "The attach status of the vepfs file system.", + }, + }, + } + return resource +} + +func resourceVolcengineVepfsMountServiceAttachmentCreate(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsMountServiceAttachmentService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Create(service, d, ResourceVolcengineVepfsMountServiceAttachment()) + if err != nil { + return fmt.Errorf("error on creating vepfs_mount_service_attachment %q, %s", d.Id(), err) + } + return resourceVolcengineVepfsMountServiceAttachmentRead(d, meta) +} + +func resourceVolcengineVepfsMountServiceAttachmentRead(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsMountServiceAttachmentService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Read(service, d, ResourceVolcengineVepfsMountServiceAttachment()) + if err != nil { + return fmt.Errorf("error on reading vepfs_mount_service_attachment %q, %s", d.Id(), err) + } + return err +} + +func resourceVolcengineVepfsMountServiceAttachmentUpdate(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsMountServiceAttachmentService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Update(service, d, ResourceVolcengineVepfsMountServiceAttachment()) + if err != nil { + return fmt.Errorf("error on updating vepfs_mount_service_attachment %q, %s", d.Id(), err) + } + return resourceVolcengineVepfsMountServiceAttachmentRead(d, meta) +} + +func resourceVolcengineVepfsMountServiceAttachmentDelete(d *schema.ResourceData, meta interface{}) (err error) { + service := NewVepfsMountServiceAttachmentService(meta.(*ve.SdkClient)) + err = service.Dispatcher.Delete(service, d, ResourceVolcengineVepfsMountServiceAttachment()) + if err != nil { + return fmt.Errorf("error on deleting vepfs_mount_service_attachment %q, %s", d.Id(), err) + } + return err +} + +var attachmentImporter = func(data *schema.ResourceData, i interface{}) ([]*schema.ResourceData, error) { + items := strings.Split(data.Id(), ":") + if len(items) != 2 { + return []*schema.ResourceData{data}, fmt.Errorf("import id must split with ':'") + } + if err := data.Set("mount_service_id", items[0]); err != nil { + return []*schema.ResourceData{data}, err + } + if err := data.Set("file_system_id", items[1]); err != nil { + return []*schema.ResourceData{data}, err + } + return []*schema.ResourceData{data}, nil +} diff --git a/volcengine/vepfs/vepfs_mount_service_attachment/service_volcengine_vepfs_mount_service_attachment.go b/volcengine/vepfs/vepfs_mount_service_attachment/service_volcengine_vepfs_mount_service_attachment.go new file mode 100644 index 0000000..50c766c --- /dev/null +++ b/volcengine/vepfs/vepfs_mount_service_attachment/service_volcengine_vepfs_mount_service_attachment.go @@ -0,0 +1,321 @@ +package vepfs_mount_service_attachment + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/mitchellh/copystructure" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + ve "github.com/volcengine/terraform-provider-volcengine/common" + "github.com/volcengine/terraform-provider-volcengine/logger" +) + +type VolcengineVepfsMountServiceAttachmentService struct { + Client *ve.SdkClient + Dispatcher *ve.Dispatcher +} + +func NewVepfsMountServiceAttachmentService(c *ve.SdkClient) *VolcengineVepfsMountServiceAttachmentService { + return &VolcengineVepfsMountServiceAttachmentService{ + Client: c, + Dispatcher: &ve.Dispatcher{}, + } +} + +func (s *VolcengineVepfsMountServiceAttachmentService) GetClient() *ve.SdkClient { + return s.Client +} + +func (s *VolcengineVepfsMountServiceAttachmentService) ReadResources(m map[string]interface{}) (data []interface{}, err error) { + var ( + newCondition map[string]interface{} + resp *map[string]interface{} + results interface{} + ok bool + ) + return ve.WithPageNumberQuery(m, "PageSize", "PageNumber", 100, 1, func(condition map[string]interface{}) ([]interface{}, error) { + action := "DescribeMountServices" + + deepCopyValue, err := copystructure.Copy(condition) + if err != nil { + return data, fmt.Errorf(" DeepCopy condition error: %v ", err) + } + if newCondition, ok = deepCopyValue.(map[string]interface{}); !ok { + return data, fmt.Errorf(" DeepCopy condition error: newCondition is not map ") + } + + // 处理 Filters + if filters, exists := condition["Filters"]; exists { + filtersMap, ok := filters.(map[string]interface{}) + if !ok { + return data, fmt.Errorf(" Filters is not map ") + } + // 处理 Filters.Status,逗号分离 + if statuses, exists := filtersMap["Status"]; exists { + statusArr, ok := statuses.([]interface{}) + if !ok { + return data, fmt.Errorf(" Filters.Status is not slice ") + } + newStatus := make([]string, 0) + for _, status := range statusArr { + newStatus = append(newStatus, status.(string)) + } + newCondition["Filters"].(map[string]interface{})["Status"] = strings.Join(newStatus, ",") + } + + newFilters := make([]interface{}, 0) + for key, value := range newCondition["Filters"].(map[string]interface{}) { + newFilters = append(newFilters, map[string]interface{}{ + "Key": key, + "Value": value, + }) + } + newCondition["Filters"] = newFilters + } + + bytes, _ := json.Marshal(newCondition) + logger.Debug(logger.ReqFormat, action, string(bytes)) + if newCondition == nil { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), nil) + if err != nil { + return data, err + } + } else { + resp, err = s.Client.UniversalClient.DoCall(getUniversalInfo(action), &newCondition) + if err != nil { + return data, err + } + } + respBytes, _ := json.Marshal(resp) + logger.Debug(logger.RespFormat, action, newCondition, string(respBytes)) + results, err = ve.ObtainSdkValue("Result.MountServices", *resp) + if err != nil { + return data, err + } + if results == nil { + results = []interface{}{} + } + if data, ok = results.([]interface{}); !ok { + return data, errors.New("Result.MountServices is not Slice") + } + return data, err + }) +} + +func (s *VolcengineVepfsMountServiceAttachmentService) ReadResource(resourceData *schema.ResourceData, id string) (data map[string]interface{}, err error) { + var ( + results []interface{} + ok bool + ) + if id == "" { + id = s.ReadResourceId(resourceData.Id()) + } + ids := strings.Split(id, ":") + if len(ids) != 2 { + return data, fmt.Errorf("invalid mount service attachment Id: %s", id) + } + + req := map[string]interface{}{ + "Filters": map[string]interface{}{ + "MountServiceId": ids[0], + "FileSystemId": ids[1], + }, + } + results, err = s.ReadResources(req) + if err != nil { + return data, err + } + for _, v := range results { + if data, ok = v.(map[string]interface{}); !ok { + return data, errors.New("Value is not map ") + } + } + if len(data) == 0 { + return data, fmt.Errorf("vepfs_mount_service %s not exist ", id) + } + + attached := false + if fileSystems, exist := data["AttachFileSystems"]; exist { + fileSystemArr, ok := fileSystems.([]interface{}) + if !ok { + return data, fmt.Errorf(" mount serive fileSystems is not slice ") + } + for _, v := range fileSystemArr { + fileSystemMap, ok := v.(map[string]interface{}) + if !ok { + return data, fmt.Errorf(" mount serive fileSystems Value is not map ") + } + if fileSystemMap["FileSystemId"] == ids[1] { + data["CustomerPath"] = fileSystemMap["CustomerPath"] + data["AttachStatus"] = fileSystemMap["Status"] + attached = true + break + } + } + } else { + data["AttachFileSystems"] = []interface{}{} + } + if !attached { + return data, fmt.Errorf("vepfs_mount_service_attachment %s not exist ", id) + } + + return data, err +} + +func (s *VolcengineVepfsMountServiceAttachmentService) RefreshResourceState(resourceData *schema.ResourceData, target []string, timeout time.Duration, id string) *resource.StateChangeConf { + return &resource.StateChangeConf{ + Pending: []string{}, + Delay: 1 * time.Second, + MinTimeout: 1 * time.Second, + Target: target, + Timeout: timeout, + Refresh: func() (result interface{}, state string, err error) { + var ( + d map[string]interface{} + status interface{} + failStates []string + ) + failStates = append(failStates, "AttachError") + failStates = append(failStates, "DetachError") + d, err = s.ReadResource(resourceData, id) + if err != nil { + return nil, "", err + } + status, err = ve.ObtainSdkValue("AttachStatus", d) + if err != nil { + return nil, "", err + } + for _, v := range failStates { + if v == status.(string) { + return nil, "", fmt.Errorf("vepfs_mount_service_attachment status error, status: %s", status.(string)) + } + } + return d, status.(string), err + }, + } +} + +func (VolcengineVepfsMountServiceAttachmentService) WithResourceResponseHandlers(d map[string]interface{}) []ve.ResourceResponseHandler { + handler := func() (map[string]interface{}, map[string]ve.ResponseConvert, error) { + return d, nil, nil + } + return []ve.ResourceResponseHandler{handler} +} + +func (s *VolcengineVepfsMountServiceAttachmentService) CreateResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "AttachMountServiceToSelfFileSystem", + ConvertMode: ve.RequestConvertInConvert, + ContentType: ve.ContentTypeJson, + Convert: map[string]ve.RequestConvert{ + "mount_service_id": { + TargetField: "MountServiceId", + }, + "file_system_id": { + TargetField: "FileSystemId", + }, + "customer_path": { + TargetField: "CustomerPath", + }, + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + resp, err := s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + logger.Debug(logger.RespFormat, call.Action, resp, err) + return resp, err + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + mountServiceId := d.Get("mount_service_id") + fileSystemId := d.Get("file_system_id") + d.SetId(mountServiceId.(string) + ":" + fileSystemId.(string)) + return nil + }, + Refresh: &ve.StateRefresh{ + Target: []string{"Attached"}, + Timeout: resourceData.Timeout(schema.TimeoutCreate), + }, + // 必须顺序执行,否则并发失败 + LockId: func(d *schema.ResourceData) string { + return "lock-VePFS-attachment" + }, + }, + } + return []ve.Callback{callback} +} + +func (s *VolcengineVepfsMountServiceAttachmentService) ModifyResource(resourceData *schema.ResourceData, resource *schema.Resource) []ve.Callback { + return []ve.Callback{} +} + +func (s *VolcengineVepfsMountServiceAttachmentService) RemoveResource(resourceData *schema.ResourceData, r *schema.Resource) []ve.Callback { + callback := ve.Callback{ + Call: ve.SdkCall{ + Action: "DetachMountServiceFromSelfFileSystem", + ConvertMode: ve.RequestConvertIgnore, + ContentType: ve.ContentTypeJson, + BeforeCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (bool, error) { + ids := strings.Split(d.Id(), ":") + if len(ids) != 2 { + return false, fmt.Errorf("invalid mount service attachment Id: %s", d.Id()) + } + + (*call.SdkParam)["MountServiceId"] = ids[0] + (*call.SdkParam)["FileSystemId"] = ids[1] + return true, nil + }, + ExecuteCall: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall) (*map[string]interface{}, error) { + logger.Debug(logger.RespFormat, call.Action, call.SdkParam) + return s.Client.UniversalClient.DoCall(getUniversalInfo(call.Action), call.SdkParam) + }, + AfterCall: func(d *schema.ResourceData, client *ve.SdkClient, resp *map[string]interface{}, call ve.SdkCall) error { + return ve.CheckResourceUtilRemoved(d, s.ReadResource, 5*time.Minute) + }, + CallError: func(d *schema.ResourceData, client *ve.SdkClient, call ve.SdkCall, baseErr error) error { + //出现错误后重试 + return resource.Retry(15*time.Minute, func() *resource.RetryError { + _, callErr := s.ReadResource(d, "") + if callErr != nil { + if ve.ResourceNotFoundError(callErr) { + return nil + } else { + return resource.NonRetryableError(fmt.Errorf("error on reading vepfs mount service attachment on delete %q, %w", d.Id(), callErr)) + } + } + _, callErr = call.ExecuteCall(d, client, call) + if callErr == nil { + return nil + } + return resource.RetryableError(callErr) + }) + }, + // 必须顺序执行,否则并发失败 + LockId: func(d *schema.ResourceData) string { + return "lock-VePFS-attachment" + }, + }, + } + return []ve.Callback{callback} +} + +func (s *VolcengineVepfsMountServiceAttachmentService) DatasourceResources(*schema.ResourceData, *schema.Resource) ve.DataSourceInfo { + return ve.DataSourceInfo{} +} + +func (s *VolcengineVepfsMountServiceAttachmentService) ReadResourceId(id string) string { + return id +} + +func getUniversalInfo(actionName string) ve.UniversalInfo { + return ve.UniversalInfo{ + ServiceName: "vepfs", + Version: "2022-01-01", + HttpMethod: ve.POST, + ContentType: ve.ApplicationJSON, + Action: actionName, + } +} From fc4fe267c8029c536a706b2c81cc975eec66fbe2 Mon Sep 17 00:00:00 2001 From: "maoshuai.17" Date: Fri, 30 Aug 2024 15:55:24 +0800 Subject: [PATCH 2/2] feat: update docs and version --- common/common_volcengine_version.go | 2 +- docgen/main.go | 1 + .../resource_volcengine_vepfs_file_system.go | 2 +- .../docs/d/vepfs_file_systems.html.markdown | 89 +++++++++++++++++++ website/docs/d/vepfs_filesets.html.markdown | 82 +++++++++++++++++ .../docs/d/vepfs_mount_services.html.markdown | 71 +++++++++++++++ .../docs/r/vepfs_file_system.html.markdown | 82 +++++++++++++++++ website/docs/r/vepfs_fileset.html.markdown | 74 +++++++++++++++ .../docs/r/vepfs_mount_service.html.markdown | 65 ++++++++++++++ ...pfs_mount_service_attachment.html.markdown | 68 ++++++++++++++ website/volcengine.erb | 36 ++++++++ 11 files changed, 570 insertions(+), 2 deletions(-) create mode 100644 website/docs/d/vepfs_file_systems.html.markdown create mode 100644 website/docs/d/vepfs_filesets.html.markdown create mode 100644 website/docs/d/vepfs_mount_services.html.markdown create mode 100644 website/docs/r/vepfs_file_system.html.markdown create mode 100644 website/docs/r/vepfs_fileset.html.markdown create mode 100644 website/docs/r/vepfs_mount_service.html.markdown create mode 100644 website/docs/r/vepfs_mount_service_attachment.html.markdown diff --git a/common/common_volcengine_version.go b/common/common_volcengine_version.go index ba1a61c..0b9c186 100644 --- a/common/common_volcengine_version.go +++ b/common/common_volcengine_version.go @@ -2,5 +2,5 @@ package common const ( TerraformProviderName = "terraform-provider-volcengine" - TerraformProviderVersion = "0.0.150" + TerraformProviderVersion = "0.0.151" ) diff --git a/docgen/main.go b/docgen/main.go index 6906790..6bff85b 100644 --- a/docgen/main.go +++ b/docgen/main.go @@ -161,6 +161,7 @@ var resourceKeys = map[string]string{ "cloud_identity": "CLOUD_IDENTITY", "kafka": "KAFKA", "private_zone": "PRIVATE_ZONE", + "vepfs": "VEPFS", } type Products struct { diff --git a/volcengine/vepfs/vepfs_file_system/resource_volcengine_vepfs_file_system.go b/volcengine/vepfs/vepfs_file_system/resource_volcengine_vepfs_file_system.go index 5b839f0..e014032 100644 --- a/volcengine/vepfs/vepfs_file_system/resource_volcengine_vepfs_file_system.go +++ b/volcengine/vepfs/vepfs_file_system/resource_volcengine_vepfs_file_system.go @@ -48,7 +48,7 @@ func ResourceVolcengineVepfsFileSystem() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - Description: "The store type of the vepfs file system. Valid values: `Advance_100`, `Performance` , `Intelligent_Computing`.", + Description: "The store type of the vepfs file system. Valid values: `Advance_100`, `Performance`, `Intelligent_Computing`.", }, "description": { Type: schema.TypeString, diff --git a/website/docs/d/vepfs_file_systems.html.markdown b/website/docs/d/vepfs_file_systems.html.markdown new file mode 100644 index 0000000..8a3345d --- /dev/null +++ b/website/docs/d/vepfs_file_systems.html.markdown @@ -0,0 +1,89 @@ +--- +subcategory: "VEPFS" +layout: "volcengine" +page_title: "Volcengine: volcengine_vepfs_file_systems" +sidebar_current: "docs-volcengine-datasource-vepfs_file_systems" +description: |- + Use this data source to query detailed information of vepfs file systems +--- +# volcengine_vepfs_file_systems +Use this data source to query detailed information of vepfs file systems +## Example Usage +```hcl +resource "volcengine_vpc" "foo" { + vpc_name = "acc-test-vpc" + cidr_block = "172.16.0.0/16" +} + +resource "volcengine_subnet" "foo" { + subnet_name = "acc-test-subnet" + cidr_block = "172.16.0.0/24" + zone_id = "cn-beijing-a" + vpc_id = volcengine_vpc.foo.id +} + +resource "volcengine_vepfs_file_system" "foo" { + file_system_name = "acc-test-file-system" + subnet_id = volcengine_subnet.foo.id + store_type = "Advance_100" + description = "tf-test" + capacity = 12 + project = "default" + enable_restripe = false + tags { + key = "k1" + value = "v1" + } +} + +data "volcengine_vepfs_file_systems" "foo" { + ids = [volcengine_vepfs_file_system.foo.id] +} +``` +## Argument Reference +The following arguments are supported: +* `file_system_name` - (Optional) The Name of Vepfs File System. This field support fuzzy query. +* `ids` - (Optional) A list of Vepfs File System IDs. +* `name_regex` - (Optional) A Name Regex of Resource. +* `output_file` - (Optional) File name where to save data source results. +* `project` - (Optional) The project of Vepfs File System. +* `status` - (Optional) The query status list of Vepfs File System. +* `store_type` - (Optional) The Store Type of Vepfs File System. +* `zone_id` - (Optional) The zone id of File System. + +## Attributes Reference +In addition to all arguments above, the following attributes are exported: +* `file_systems` - The collection of query. + * `account_id` - The id of the account. + * `bandwidth` - The bandwidth info of the vepfs file system. + * `capacity_info` - The capacity info of the vepfs file system. + * `total_tib` - The total size. Unit: TiB. + * `used_gib` - The used size. Unit: GiB. + * `charge_status` - The charge status of the vepfs file system. + * `charge_type` - The charge type of the vepfs file system. + * `create_time` - The create time of the vepfs file system. + * `description` - The description of the vepfs file system. + * `expire_time` - The expire time of the vepfs file system. + * `file_system_id` - The id of the vepfs file system. + * `file_system_name` - The name of the vepfs file system. + * `file_system_type` - The type of the vepfs file system. + * `free_time` - The free time of the vepfs file system. + * `id` - The id of the vepfs file system. + * `last_modify_time` - The last modify time of the vepfs file system. + * `project` - The project name of the vepfs file system. + * `protocol_type` - The protocol type of the vepfs file system. + * `region_id` - The id of the region. + * `status` - The status of the vepfs file system. + * `stop_service_time` - The stop service time of the vepfs file system. + * `store_type_cn` - The store type cn name of the vepfs file system. + * `store_type` - The store type of the vepfs file system. + * `tags` - The tags of the vepfs file system. + * `key` - The Key of Tags. + * `type` - The Type of Tags. + * `value` - The Value of Tags. + * `version` - The version info of the vepfs file system. + * `zone_id` - The id of the zone. + * `zone_name` - The name of the zone. +* `total_count` - The total count of query. + + diff --git a/website/docs/d/vepfs_filesets.html.markdown b/website/docs/d/vepfs_filesets.html.markdown new file mode 100644 index 0000000..13417a0 --- /dev/null +++ b/website/docs/d/vepfs_filesets.html.markdown @@ -0,0 +1,82 @@ +--- +subcategory: "VEPFS" +layout: "volcengine" +page_title: "Volcengine: volcengine_vepfs_filesets" +sidebar_current: "docs-volcengine-datasource-vepfs_filesets" +description: |- + Use this data source to query detailed information of vepfs filesets +--- +# volcengine_vepfs_filesets +Use this data source to query detailed information of vepfs filesets +## Example Usage +```hcl +resource "volcengine_vpc" "foo" { + vpc_name = "acc-test-vpc" + cidr_block = "172.16.0.0/16" +} + +resource "volcengine_subnet" "foo" { + subnet_name = "acc-test-subnet" + cidr_block = "172.16.0.0/24" + zone_id = "cn-beijing-a" + vpc_id = volcengine_vpc.foo.id +} + +resource "volcengine_vepfs_file_system" "foo" { + file_system_name = "acc-test-file-system" + subnet_id = volcengine_subnet.foo.id + store_type = "Advance_100" + description = "tf-test" + capacity = 12 + project = "default" + enable_restripe = false + tags { + key = "k1" + value = "v1" + } +} + +resource "volcengine_vepfs_fileset" "foo" { + file_system_id = volcengine_vepfs_file_system.foo.id + fileset_name = "acc-test-fileset" + fileset_path = "/tf-test/" + max_iops = 100 + max_bandwidth = 10 + file_limit = 20 + capacity_limit = 30 +} + +data "volcengine_vepfs_filesets" "foo" { + file_system_id = volcengine_vepfs_file_system.foo.id + fileset_id = volcengine_vepfs_fileset.foo.id +} +``` +## Argument Reference +The following arguments are supported: +* `file_system_id` - (Required) The id of Vepfs File System. +* `fileset_id` - (Optional) The id of Vepfs Fileset. +* `fileset_name` - (Optional) The name of Vepfs Fileset. This field support fuzzy query. +* `fileset_path` - (Optional) The path of Vepfs Fileset. This field support fuzzy query. +* `name_regex` - (Optional) A Name Regex of Resource. +* `output_file` - (Optional) File name where to save data source results. +* `status` - (Optional) The query status list of Vepfs Fileset. + +## Attributes Reference +In addition to all arguments above, the following attributes are exported: +* `filesets` - The collection of query. + * `bandwidth_qos` - The bandwidth Qos of the vepfs fileset. + * `capacity_limit` - The capacity limit of the vepfs fileset. Unit: GiB. + * `capacity_used` - The used capacity of the vepfs fileset. Unit: GiB. + * `create_time` - The create time of the vepfs fileset. + * `file_limit` - Quota for the number of files or directories. A return of 0 indicates that there is no quota limit set for the number of directories after the file. + * `file_used` - The used file number of the vepfs fileset. + * `fileset_id` - The id of the vepfs fileset. + * `fileset_name` - The name of the vepfs fileset. + * `fileset_path` - The path of the vepfs fileset. + * `id` - The id of the vepfs fileset. + * `iops_qos` - The IOPS Qos of the vepfs fileset. + * `max_inode_num` - The max number of inode in the vepfs fileset. + * `status` - The status of the vepfs fileset. +* `total_count` - The total count of query. + + diff --git a/website/docs/d/vepfs_mount_services.html.markdown b/website/docs/d/vepfs_mount_services.html.markdown new file mode 100644 index 0000000..d65f503 --- /dev/null +++ b/website/docs/d/vepfs_mount_services.html.markdown @@ -0,0 +1,71 @@ +--- +subcategory: "VEPFS" +layout: "volcengine" +page_title: "Volcengine: volcengine_vepfs_mount_services" +sidebar_current: "docs-volcengine-datasource-vepfs_mount_services" +description: |- + Use this data source to query detailed information of vepfs mount services +--- +# volcengine_vepfs_mount_services +Use this data source to query detailed information of vepfs mount services +## Example Usage +```hcl +resource "volcengine_vpc" "foo" { + vpc_name = "acc-test-vpc" + cidr_block = "172.16.0.0/16" +} + +resource "volcengine_subnet" "foo" { + subnet_name = "acc-test-subnet" + cidr_block = "172.16.0.0/24" + zone_id = "cn-beijing-a" + vpc_id = volcengine_vpc.foo.id +} + +resource "volcengine_vepfs_mount_service" "foo" { + mount_service_name = "acc-test-mount-service" + subnet_id = volcengine_subnet.foo.id + node_type = "ecs.g1ie.large" + project = "default" +} + +data "volcengine_vepfs_mount_services" "foo" { + mount_service_id = volcengine_vepfs_mount_service.foo.id +} +``` +## Argument Reference +The following arguments are supported: +* `file_system_id` - (Optional) The id of Vepfs File System. +* `mount_service_id` - (Optional) The id of mount service. +* `mount_service_name` - (Optional) The name of mount service. This field support fuzzy query. +* `name_regex` - (Optional) A Name Regex of Resource. +* `output_file` - (Optional) File name where to save data source results. +* `status` - (Optional) The query status list of mount service. + +## Attributes Reference +In addition to all arguments above, the following attributes are exported: +* `mount_services` - The collection of query. + * `account_id` - The account id of the mount service. + * `attach_file_systems` - The attached file system info of the mount service. + * `account_id` - The account id of the vepfs file system. + * `customer_path` - The id of the vepfs file system. + * `file_system_id` - The id of the vepfs file system. + * `file_system_name` - The name of the vepfs file system. + * `status` - The status of the vepfs file system. + * `create_time` - The created time of the mount service. + * `id` - The id of the mount service. + * `mount_service_id` - The id of the mount service. + * `mount_service_name` - The name of the mount service. + * `nodes` - The nodes info of the mount service. + * `default_password` - The default password of ecs instance. + * `node_id` - The id of ecs instance. + * `project` - The project of the mount service. + * `region_id` - The region id of the mount service. + * `status` - The status of the mount service. + * `subnet_id` - The subnet id of the mount service. + * `vpc_id` - The vpc id of the mount service. + * `zone_id` - The zone id of the mount service. + * `zone_name` - The zone name of the mount service. +* `total_count` - The total count of query. + + diff --git a/website/docs/r/vepfs_file_system.html.markdown b/website/docs/r/vepfs_file_system.html.markdown new file mode 100644 index 0000000..5d33637 --- /dev/null +++ b/website/docs/r/vepfs_file_system.html.markdown @@ -0,0 +1,82 @@ +--- +subcategory: "VEPFS" +layout: "volcengine" +page_title: "Volcengine: volcengine_vepfs_file_system" +sidebar_current: "docs-volcengine-resource-vepfs_file_system" +description: |- + Provides a resource to manage vepfs file system +--- +# volcengine_vepfs_file_system +Provides a resource to manage vepfs file system +## Example Usage +```hcl +resource "volcengine_vpc" "foo" { + vpc_name = "acc-test-vpc" + cidr_block = "172.16.0.0/16" +} + +resource "volcengine_subnet" "foo" { + subnet_name = "acc-test-subnet" + cidr_block = "172.16.0.0/24" + zone_id = "cn-beijing-a" + vpc_id = volcengine_vpc.foo.id +} + +resource "volcengine_vepfs_file_system" "foo" { + file_system_name = "acc-test-file-system" + subnet_id = volcengine_subnet.foo.id + store_type = "Advance_100" + description = "tf-test" + capacity = 12 + project = "default" + enable_restripe = false + tags { + key = "k1" + value = "v1" + } +} +``` +## Argument Reference +The following arguments are supported: +* `capacity` - (Required) The capacity of the vepfs file system. +* `file_system_name` - (Required) The name of the vepfs file system. +* `store_type` - (Required, ForceNew) The store type of the vepfs file system. Valid values: `Advance_100`, `Performance`, `Intelligent_Computing`. +* `subnet_id` - (Required, ForceNew) The subnet id of the vepfs file system. +* `description` - (Optional) The description info of the vepfs file system. +* `enable_restripe` - (Optional) Whether to enable data balance after capacity expansion. This filed is valid only when expanding capacity. +* `project` - (Optional, ForceNew) The project of the vepfs file system. +* `tags` - (Optional) Tags. + +The `tags` object supports the following: + +* `key` - (Required) The Key of Tags. +* `value` - (Required) The Value of Tags. + +## Attributes Reference +In addition to all arguments above, the following attributes are exported: +* `id` - ID of the resource. +* `account_id` - The id of the account. +* `bandwidth` - The bandwidth info of the vepfs file system. +* `charge_status` - The charge status of the vepfs file system. +* `charge_type` - The charge type of the vepfs file system. +* `create_time` - The create time of the vepfs file system. +* `expire_time` - The expire time of the vepfs file system. +* `file_system_type` - The type of the vepfs file system. +* `free_time` - The free time of the vepfs file system. +* `last_modify_time` - The last modify time of the vepfs file system. +* `protocol_type` - The protocol type of the vepfs file system. +* `region_id` - The id of the region. +* `status` - The status of the vepfs file system. +* `stop_service_time` - The stop service time of the vepfs file system. +* `store_type_cn` - The store type cn name of the vepfs file system. +* `version` - The version info of the vepfs file system. +* `zone_id` - The id of the zone. +* `zone_name` - The name of the zone. + + +## Import +VepfsFileSystem can be imported using the id, e.g. +``` +$ terraform import volcengine_vepfs_file_system.default resource_id +``` + diff --git a/website/docs/r/vepfs_fileset.html.markdown b/website/docs/r/vepfs_fileset.html.markdown new file mode 100644 index 0000000..4c326e6 --- /dev/null +++ b/website/docs/r/vepfs_fileset.html.markdown @@ -0,0 +1,74 @@ +--- +subcategory: "VEPFS" +layout: "volcengine" +page_title: "Volcengine: volcengine_vepfs_fileset" +sidebar_current: "docs-volcengine-resource-vepfs_fileset" +description: |- + Provides a resource to manage vepfs fileset +--- +# volcengine_vepfs_fileset +Provides a resource to manage vepfs fileset +## Example Usage +```hcl +resource "volcengine_vpc" "foo" { + vpc_name = "acc-test-vpc" + cidr_block = "172.16.0.0/16" +} + +resource "volcengine_subnet" "foo" { + subnet_name = "acc-test-subnet" + cidr_block = "172.16.0.0/24" + zone_id = "cn-beijing-a" + vpc_id = volcengine_vpc.foo.id +} + +resource "volcengine_vepfs_file_system" "foo" { + file_system_name = "acc-test-file-system" + subnet_id = volcengine_subnet.foo.id + store_type = "Advance_100" + description = "tf-test" + capacity = 12 + project = "default" + enable_restripe = false + tags { + key = "k1" + value = "v1" + } +} + +resource "volcengine_vepfs_fileset" "foo" { + file_system_id = volcengine_vepfs_file_system.foo.id + fileset_name = "acc-test-fileset" + fileset_path = "/tf-test/" + max_iops = 100 + max_bandwidth = 10 + file_limit = 20 + capacity_limit = 30 +} +``` +## Argument Reference +The following arguments are supported: +* `file_system_id` - (Required, ForceNew) The id of the vepfs file system. +* `fileset_name` - (Required) The name of the vepfs fileset. +* `fileset_path` - (Required, ForceNew) The path of the vepfs fileset. +* `capacity_limit` - (Optional) The capacity limit of the vepfs fileset. Unit: Gib. +* `file_limit` - (Optional) The file number limit of the vepfs fileset. +* `max_bandwidth` - (Optional) The max bandwidth qos limit of the vepfs fileset. Unit: MB/s. +* `max_iops` - (Optional) The max IOPS qos limit of the vepfs fileset. + +## Attributes Reference +In addition to all arguments above, the following attributes are exported: +* `id` - ID of the resource. +* `capacity_used` - The used capacity of the vepfs fileset. Unit: GiB. +* `create_time` - The create time of the vepfs fileset. +* `file_used` - The used file number of the vepfs fileset. +* `max_inode_num` - The max number of inode in the vepfs fileset. +* `status` - The status of the vepfs fileset. + + +## Import +VepfsFileset can be imported using the file_system_id:fileset_id, e.g. +``` +$ terraform import volcengine_vepfs_fileset.default file_system_id:fileset_id +``` + diff --git a/website/docs/r/vepfs_mount_service.html.markdown b/website/docs/r/vepfs_mount_service.html.markdown new file mode 100644 index 0000000..25abb70 --- /dev/null +++ b/website/docs/r/vepfs_mount_service.html.markdown @@ -0,0 +1,65 @@ +--- +subcategory: "VEPFS" +layout: "volcengine" +page_title: "Volcengine: volcengine_vepfs_mount_service" +sidebar_current: "docs-volcengine-resource-vepfs_mount_service" +description: |- + Provides a resource to manage vepfs mount service +--- +# volcengine_vepfs_mount_service +Provides a resource to manage vepfs mount service +## Example Usage +```hcl +resource "volcengine_vpc" "foo" { + vpc_name = "acc-test-vpc" + cidr_block = "172.16.0.0/16" +} + +resource "volcengine_subnet" "foo" { + subnet_name = "acc-test-subnet" + cidr_block = "172.16.0.0/24" + zone_id = "cn-beijing-a" + vpc_id = volcengine_vpc.foo.id +} + +resource "volcengine_vepfs_mount_service" "foo" { + mount_service_name = "acc-test-mount-service" + subnet_id = volcengine_subnet.foo.id + node_type = "ecs.g1ie.large" + project = "default" +} +``` +## Argument Reference +The following arguments are supported: +* `mount_service_name` - (Required) The name of the mount service. +* `node_type` - (Required, ForceNew) The node type of the mount service. When importing resources, this attribute will not be imported. If this attribute is set, please use lifecycle and ignore_changes ignore changes in fields. +* `subnet_id` - (Required, ForceNew) The subnet id of the mount service. +* `project` - (Optional, ForceNew) The node type of the mount service. + +## Attributes Reference +In addition to all arguments above, the following attributes are exported: +* `id` - ID of the resource. +* `account_id` - The account id of the mount service. +* `attach_file_systems` - The attached file system info of the mount service. + * `account_id` - The account id of the vepfs file system. + * `customer_path` - The id of the vepfs file system. + * `file_system_id` - The id of the vepfs file system. + * `file_system_name` - The name of the vepfs file system. + * `status` - The status of the vepfs file system. +* `create_time` - The created time of the mount service. +* `nodes` - The nodes info of the mount service. + * `default_password` - The default password of ecs instance. + * `node_id` - The id of ecs instance. +* `region_id` - The region id of the mount service. +* `status` - The status of the mount service. +* `vpc_id` - The vpc id of the mount service. +* `zone_id` - The zone id of the mount service. +* `zone_name` - The zone name of the mount service. + + +## Import +VepfsMountService can be imported using the id, e.g. +``` +$ terraform import volcengine_vepfs_mount_service.default resource_id +``` + diff --git a/website/docs/r/vepfs_mount_service_attachment.html.markdown b/website/docs/r/vepfs_mount_service_attachment.html.markdown new file mode 100644 index 0000000..ebb5cf6 --- /dev/null +++ b/website/docs/r/vepfs_mount_service_attachment.html.markdown @@ -0,0 +1,68 @@ +--- +subcategory: "VEPFS" +layout: "volcengine" +page_title: "Volcengine: volcengine_vepfs_mount_service_attachment" +sidebar_current: "docs-volcengine-resource-vepfs_mount_service_attachment" +description: |- + Provides a resource to manage vepfs mount service attachment +--- +# volcengine_vepfs_mount_service_attachment +Provides a resource to manage vepfs mount service attachment +## Example Usage +```hcl +resource "volcengine_vpc" "foo" { + vpc_name = "acc-test-vpc" + cidr_block = "172.16.0.0/16" +} + +resource "volcengine_subnet" "foo" { + subnet_name = "acc-test-subnet" + cidr_block = "172.16.0.0/24" + zone_id = "cn-beijing-a" + vpc_id = volcengine_vpc.foo.id +} + +resource "volcengine_vepfs_file_system" "foo" { + file_system_name = "acc-test-file-system" + subnet_id = volcengine_subnet.foo.id + store_type = "Advance_100" + description = "tf-test" + capacity = 12 + project = "default" + enable_restripe = false + tags { + key = "k1" + value = "v1" + } +} + +resource "volcengine_vepfs_mount_service" "foo" { + mount_service_name = "acc-test-mount-service" + subnet_id = volcengine_subnet.foo.id + node_type = "ecs.g1ie.large" + project = "default" +} + +resource "volcengine_vepfs_mount_service_attachment" "foo" { + mount_service_id = volcengine_vepfs_mount_service.foo.id + file_system_id = volcengine_vepfs_file_system.foo.id +} +``` +## Argument Reference +The following arguments are supported: +* `file_system_id` - (Required, ForceNew) The id of the vepfs file system. +* `mount_service_id` - (Required, ForceNew) The id of the mount service. +* `customer_path` - (Optional, ForceNew) The custom mount directory, the default value is file system id. + +## Attributes Reference +In addition to all arguments above, the following attributes are exported: +* `id` - ID of the resource. +* `attach_status` - The attach status of the vepfs file system. + + +## Import +VepfsMountServiceAttachment can be imported using the mount_service_id:file_system_id, e.g. +``` +$ terraform import volcengine_vepfs_mount_service_attachment.default mount_service_id:file_system_id +``` + diff --git a/website/volcengine.erb b/website/volcengine.erb index 65d053b..317fa5b 100644 --- a/website/volcengine.erb +++ b/website/volcengine.erb @@ -1630,6 +1630,42 @@ +
  • + VEPFS + +
  • VKE