diff --git a/docs/data-sources/obs_bucket_object.md b/docs/data-sources/obs_bucket_object.md
new file mode 100644
index 00000000..a2d27d24
--- /dev/null
+++ b/docs/data-sources/obs_bucket_object.md
@@ -0,0 +1,48 @@
+---
+subcategory: "Object Storage Service (OBS)"
+---
+
+# flexibleengine_obs_bucket_object
+
+Use this data source to get info of special FlexibleEngine obs object.
+
+```hcl
+data "flexibleengine_obs_bucket_object" "object" {
+ bucket = "my-test-bucket"
+ key = "new-key"
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `region` - (Optional, String) The region in which to obtain the OBS object. If omitted, the provider-level region will
+ be used.
+
+* `bucket` - (Required, String) The name of the bucket to put the file in.
+
+* `key` - (Required, String) The name of the object once it is in the bucket.
+
+## Attribute Reference
+
+In addition to all arguments above, the following attributes are exported:
+
+* `id` - the `key` of the resource supplied above.
+
+* `etag` - the ETag generated for the object (an MD5 sum of the object content). When the object is encrypted on the
+ server side, the ETag value is not the MD5 value of the object, but the unique identifier calculated through the
+ server-side encryption.
+
+* `size` - the size of the object in bytes.
+
+* `version_id` - a unique version ID value for the object, if bucket versioning is enabled.
+
+* `storage_class` - specifies the storage class of the object.
+
+* `content_type` - a standard MIME type describing the format of the object data, e.g. application/octet-stream. All
+ Valid MIME Types are valid for this input.
+
+* `body` - The content of an object which is available only for objects which have a human-readable Content-Type
+ (text/* and application/json) and smaller than **64KB**. This is to prevent printing unsafe characters and
+ potentially downloading large amount of data.
diff --git a/docs/data-sources/obs_buckets.md b/docs/data-sources/obs_buckets.md
new file mode 100644
index 00000000..a8b92e52
--- /dev/null
+++ b/docs/data-sources/obs_buckets.md
@@ -0,0 +1,44 @@
+---
+subcategory: "Object Storage Service (OBS)"
+---
+
+# flexibleengine_obs_buckets
+
+Use this data source to get all OBS buckets.
+
+```hcl
+data "flexibleengine_obs_buckets" "buckets" {
+ bucket = "your-bucket-name"
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `region` - (Optional, String) The region in which to obtain the OBS bucket.
+ If omitted, the provider-level region will be used.
+
+* `bucket` - (Optional, String) The name of the OBS bucket.
+
+* `enterprise_project_id` - (Optional, String) The enterprise project id of the OBS bucket.
+
+## Attribute Reference
+
+In addition to all arguments above, the following attributes are exported:
+
+* `id` - The ID of the list.
+
+* `buckets` - A list of OBS buckets.
+
+The `buckets` block supports:
+
+* `region` - The region where the OBS bucket belongs.
+
+* `bucket` - The name of the OBS bucket.
+
+* `enterprise_project_id` - The enterprise project id of the OBS bucket.
+
+* `storage_class` - The storage class of the OBS bucket.
+
+* `created_at` - The date when the OBS bucket was created.
diff --git a/docs/resources/obs_bucket_object_acl.md b/docs/resources/obs_bucket_object_acl.md
new file mode 100644
index 00000000..e628fffa
--- /dev/null
+++ b/docs/resources/obs_bucket_object_acl.md
@@ -0,0 +1,104 @@
+---
+subcategory: "Object Storage Service (OBS)"
+---
+
+# flexibleengine_obs_bucket_object_acl
+
+Manages an OBS bucket object acl resource within FlexibleEngine.
+
+-> **NOTE:** When creating or updating the OBS bucket object acl, the original object acl will be overwritten. When
+deleting the OBS bucket object acl, only the owner permissions will be retained, and the other permissions will be
+removed.
+
+## Example Usage
+
+```hcl
+variable "bucket" {}
+variable "key" {}
+variable "account1" {}
+variable "account2" {}
+
+resource "flexibleengine_obs_bucket_object_acl" "test" {
+ bucket = var.bucket
+ key = var.key
+
+ account_permission {
+ access_to_object = ["READ"]
+ access_to_acl = ["READ_ACP", "WRITE_ACP"]
+ account_id = var.account1
+ }
+
+ account_permission {
+ access_to_object = ["READ"]
+ access_to_acl = ["READ_ACP"]
+ account_id = var.account2
+ }
+
+ public_permission {
+ access_to_acl = ["READ_ACP", "WRITE_ACP"]
+ }
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `region` - (Optional, String, ForceNew) Specifies the region in which to create the resource.
+ If omitted, the provider-level region will be used.
+
+ Changing this parameter will create a new resource.
+
+* `bucket` - (Required, String, ForceNew) Specifies the name of the bucket which the object belongs to.
+
+ Changing this parameter will create a new resource.
+
+* `key` - (Required, String, ForceNew) Specifies the name of the object to which to set the acl.
+
+ Changing this parameter will create a new resource.
+
+* `public_permission` - (Optional, List) Specifies the object public permission.
+ The [permission_struct](#OBSBucketObjectAcl_permission_struct) structure is documented below.
+
+* `account_permission` - (Optional, List) Specifies the object account permissions.
+ The [account_permission_struct](#OBSBucketObjectAcl_account_permission_struct) structure is documented below.
+
+
+The `permission_struct` block supports:
+
+* `access_to_object` - (Optional, List) Specifies the access to object. Only **READ** supported.
+
+* `access_to_acl` - (Optional, List) Specifies the access to acl. Valid values are **READ_ACP** and **WRITE_ACP**.
+
+
+The `account_permission_struct` block supports:
+
+* `account_id` - (Required, String) Specifies the account id to authorize. The account id cannot be the object owner,
+ and must be unique.
+
+* `access_to_object` - (Optional, List) Specifies the access to object. Only **READ** supported.
+
+* `access_to_acl` - (Optional, List) Specifies the access to acl. Valid values are **READ_ACP** and **WRITE_ACP**.
+
+## Attribute Reference
+
+In addition to all arguments above, the following attributes are exported:
+
+* `id` - The name of the bucket object key.
+* `owner_permission` - The object owner permission information.
+ The [owner_permission_struct](#OBSBucketObjectAcl_owner_permission_struct) structure is documented below.
+
+
+The `owner_permission_struct` block supports:
+
+* `access_to_object` - The owner object permissions.
+
+* `access_to_acl` - The owner acl permissions.
+
+## Import
+
+The obs bucket object acl can be imported using `bucket` and `key`, separated by a slash, e.g.
+
+```bash
+$ terraform import flexibleengine_obs_bucket_object_acl.test /
+```
diff --git a/docs/resources/obs_bucket_policy.md b/docs/resources/obs_bucket_policy.md
new file mode 100644
index 00000000..8497afb6
--- /dev/null
+++ b/docs/resources/obs_bucket_policy.md
@@ -0,0 +1,103 @@
+---
+subcategory: "Object Storage Service (OBS)"
+---
+
+# flexibleengine_obs_bucket_policy
+
+Attaches a policy to an OBS bucket resource.
+
+-> **NOTE:** When creating or updating the OBS bucket policy, the original policy will be overwritten.
+
+## Example Usage
+
+### Policy with OBS format
+
+```hcl
+resource "flexibleengine_obs_bucket" "bucket" {
+ bucket = "my-test-bucket"
+}
+
+resource "flexibleengine_obs_bucket_policy" "policy" {
+ bucket = flexibleengine_obs_bucket.bucket.id
+ policy = <`, e.g.
+
+```
+$ terraform import flexibleengine_obs_bucket_policy.policy
+```
+
+S3 foramt bucket policy can be imported using the `` and "s3" by a slash, e.g.
+
+```
+$ terraform import flexibleengine_obs_bucket_policy.s3_policy /s3
+```
diff --git a/flexibleengine/acceptance/acceptance.go b/flexibleengine/acceptance/acceptance.go
index 265460d8..13d8ef30 100644
--- a/flexibleengine/acceptance/acceptance.go
+++ b/flexibleengine/acceptance/acceptance.go
@@ -21,6 +21,7 @@ var (
OS_ACCESS_KEY = os.Getenv("OS_ACCESS_KEY")
OS_SECRET_KEY = os.Getenv("OS_SECRET_KEY")
OS_PROJECT_ID = os.Getenv("OS_PROJECT_ID")
+ OS_DOMAIN_ID = os.Getenv("OS_DOMAIN_ID")
OS_VPC_ID = os.Getenv("OS_VPC_ID")
OS_NETWORK_ID = os.Getenv("OS_NETWORK_ID")
@@ -155,3 +156,9 @@ func testAccPreCheckImsBackupId(t *testing.T) {
t.Skip("OS_IMS_BACKUP_ID must be set for IMS whole image with CBR backup id")
}
}
+
+func testAccPrecheckDomainId(t *testing.T) {
+ if OS_DOMAIN_ID == "" {
+ t.Skip("OS_DOMAIN_ID must be set for acceptance tests")
+ }
+}
diff --git a/flexibleengine/acceptance/data_source_flexibleengine_obs_bucket_object_test.go b/flexibleengine/acceptance/data_source_flexibleengine_obs_bucket_object_test.go
new file mode 100644
index 00000000..10b1e789
--- /dev/null
+++ b/flexibleengine/acceptance/data_source_flexibleengine_obs_bucket_object_test.go
@@ -0,0 +1,289 @@
+package acceptance
+
+import (
+ "fmt"
+ "os"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+
+ "github.com/chnsz/golangsdk/openstack/obs"
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config"
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance"
+)
+
+func TestAccObsBucketObjectDataSource_content(t *testing.T) {
+ rInt := acctest.RandInt()
+ dataSourceName := "data.flexibleengine_obs_bucket_object.obj"
+ resourceConf, dataSourceConf := testAccObsBucketObjectDataSource_content(rInt)
+
+ resource.ParallelTest(t, resource.TestCase{
+ PreCheck: func() {
+ testAccPreCheck(t)
+ testAccPreCheckOBS(t)
+ },
+ ProviderFactories: TestAccProviderFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: resourceConf,
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckObsBucketObjectExists("flexibleengine_obs_bucket_object.object"),
+ ),
+ },
+ {
+ Config: dataSourceConf,
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckObsObjectDataSourceExists(dataSourceName),
+ resource.TestCheckResourceAttr(dataSourceName, "content_type", "binary/octet-stream"),
+ resource.TestCheckResourceAttr(dataSourceName, "storage_class", "STANDARD"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccObsBucketObjectDataSource_source(t *testing.T) {
+ dataSourceName := "data.flexibleengine_obs_bucket_object.obj"
+ tmpFile, err := os.CreateTemp("", "tf-acc-obs-obj-source")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Remove(tmpFile.Name())
+
+ // write test data to the tempfile
+ for i := 0; i < 1024; i++ {
+ _, err := tmpFile.WriteString("test obs object file storage\n")
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ tmpFile.Close()
+
+ rInt := acctest.RandInt()
+ resourceConf, dataSourceConf := testAccObsBucketObjectDataSource_source(rInt, tmpFile.Name())
+
+ resource.ParallelTest(t, resource.TestCase{
+ PreCheck: func() {
+ testAccPreCheck(t)
+ testAccPreCheckOBS(t)
+ },
+ ProviderFactories: TestAccProviderFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: resourceConf,
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckObsBucketObjectExists("flexibleengine_obs_bucket_object.object"),
+ ),
+ },
+ {
+ Config: dataSourceConf,
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckObsObjectDataSourceExists(dataSourceName),
+ resource.TestCheckResourceAttr(dataSourceName, "content_type", "binary/octet-stream"),
+ resource.TestCheckResourceAttr(dataSourceName, "storage_class", "STANDARD"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccObsBucketObjectDataSource_allParams(t *testing.T) {
+ rInt := acctest.RandInt()
+ dataSourceName := "data.flexibleengine_obs_bucket_object.obj"
+ resourceConf, dataSourceConf := testAccObsBucketObjectDataSource_allParams(rInt)
+
+ resource.ParallelTest(t, resource.TestCase{
+ PreCheck: func() {
+ testAccPreCheck(t)
+ testAccPreCheckOBS(t)
+ },
+ ProviderFactories: TestAccProviderFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: resourceConf,
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckObsBucketObjectExists("flexibleengine_obs_bucket_object.object"),
+ ),
+ },
+ {
+ Config: dataSourceConf,
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckObsObjectDataSourceExists(dataSourceName),
+ resource.TestCheckResourceAttr(dataSourceName, "content_type", "application/json"),
+ resource.TestCheckResourceAttr(dataSourceName, "storage_class", "STANDARD"),
+ resource.TestCheckResourceAttrSet(dataSourceName, "body"),
+ ),
+ },
+ },
+ })
+}
+
+func testAccCheckObsBucketObjectExists(n string) resource.TestCheckFunc {
+ return func(s *terraform.State) error {
+ rs, ok := s.RootModule().Resources[n]
+ if !ok {
+ return fmt.Errorf("Not Found: %s", n)
+ }
+
+ if rs.Primary.ID == "" {
+ return fmt.Errorf("No OBS Bucket Object ID is set")
+ }
+
+ conf := testAccProvider.Meta().(*config.Config)
+ obsClient, err := conf.ObjectStorageClient(OS_REGION_NAME)
+ if err != nil {
+ return fmt.Errorf("Error creating OBS client: %s", err)
+ }
+
+ bucket := rs.Primary.Attributes["bucket"]
+ key := rs.Primary.Attributes["key"]
+ input := &obs.ListObjectsInput{}
+ input.Bucket = bucket
+ input.Prefix = key
+
+ resp, err := obsClient.ListObjects(input)
+ if err != nil {
+ return fmt.Errorf("Error listing objects of OBS bucket %s: %s", bucket, err)
+ }
+
+ var exist bool
+ for _, content := range resp.Contents {
+ if key == content.Key {
+ exist = true
+ break
+ }
+ }
+ if !exist {
+ return fmt.Errorf("Resource %s not found in bucket %s", rs.Primary.ID, bucket)
+ }
+
+ return nil
+ }
+}
+
+
+func testAccCheckObsObjectDataSourceExists(n string) resource.TestCheckFunc {
+ return func(s *terraform.State) error {
+ rs, ok := s.RootModule().Resources[n]
+ if !ok {
+ return fmt.Errorf("can't find OBS object data source: %s", n)
+ }
+
+ if rs.Primary.ID == "" {
+ return fmt.Errorf("OBS object data source ID not set")
+ }
+
+ bucket := rs.Primary.Attributes["bucket"]
+ key := rs.Primary.Attributes["key"]
+
+ conf := acceptance.TestAccProvider.Meta().(*config.Config)
+ obsClient, err := conf.ObjectStorageClient(OS_REGION_NAME)
+ if err != nil {
+ return fmt.Errorf("Error creating OBS client: %s", err)
+ }
+
+ respList, err := obsClient.ListObjects(&obs.ListObjectsInput{
+ Bucket: bucket,
+ ListObjsInput: obs.ListObjsInput{
+ Prefix: key,
+ },
+ })
+ if err != nil {
+ return fmt.Errorf("Error listing objects of OBS bucket %s: %s", bucket, err)
+ }
+
+ var exist bool
+ for _, content := range respList.Contents {
+ if key == content.Key {
+ exist = true
+ break
+ }
+ }
+ if !exist {
+ return fmt.Errorf("object %s not found in bucket %s", key, bucket)
+ }
+
+ return nil
+ }
+}
+
+func testAccObsBucketObjectDataSource_content(randInt int) (string, string) {
+ resource := fmt.Sprintf(`
+resource "flexibleengine_obs_bucket" "object_bucket" {
+ bucket = "tf-acc-test-bucket-%d"
+}
+
+resource "flexibleengine_obs_bucket_object" "object" {
+ bucket = flexibleengine_obs_bucket.object_bucket.bucket
+ key = "test-key-%d"
+ content = "some_bucket_content"
+}
+`, randInt, randInt)
+
+ dataSource := fmt.Sprintf(`
+%s
+
+data "flexibleengine_obs_bucket_object" "obj" {
+ bucket = "tf-acc-test-bucket-%d"
+ key = "test-key-%d"
+}`, resource, randInt, randInt)
+
+ return resource, dataSource
+}
+
+func testAccObsBucketObjectDataSource_source(randInt int, source string) (string, string) {
+ resource := fmt.Sprintf(`
+resource "flexibleengine_obs_bucket" "object_bucket" {
+ bucket = "tf-acc-test-bucket-%d"
+}
+
+resource "flexibleengine_obs_bucket_object" "object" {
+ bucket = flexibleengine_obs_bucket.object_bucket.bucket
+ key = "test-key-%d"
+ source = "%s"
+ content_type = "binary/octet-stream"
+}
+`, randInt, randInt, source)
+
+ dataSource := fmt.Sprintf(`
+%s
+
+data "flexibleengine_obs_bucket_object" "obj" {
+ bucket = "tf-acc-test-bucket-%d"
+ key = "test-key-%d"
+}`, resource, randInt, randInt)
+
+ return resource, dataSource
+}
+
+func testAccObsBucketObjectDataSource_allParams(randInt int) (string, string) {
+ resource := fmt.Sprintf(`
+resource "flexibleengine_obs_bucket" "object_bucket" {
+ bucket = "tf-acc-test-bucket-%d"
+}
+
+resource "flexibleengine_obs_bucket_object" "object" {
+ bucket = flexibleengine_obs_bucket.object_bucket.bucket
+ key = "test-key-%d"
+ acl = "private"
+ storage_class = "STANDARD"
+ encryption = true
+ content_type = "application/json"
+ content = <