diff --git a/docs/data-sources/workspace_app_image_servers.md b/docs/data-sources/workspace_app_image_servers.md new file mode 100644 index 0000000000..feffcae4b2 --- /dev/null +++ b/docs/data-sources/workspace_app_image_servers.md @@ -0,0 +1,106 @@ +--- +subcategory: "Workspace" +layout: "huaweicloud" +page_title: "HuaweiCloud: huaweicloud_workspace_app_image_servers" +description: |- + Use this data source to get the list of the image servers within HuaweiCloud. +--- + +# huaweicloud_workspace_app_image_servers + +Use this data source to get the list of the image servers within HuaweiCloud. + +## Example Usage + +### Query all image servers + +```hcl +data "huaweicloud_workspace_app_image_servers" "test" {} +``` + +### Query image server with the specified image server ID + +```hcl +variable "image_server_id" {} + +data "huaweicloud_workspace_app_image_servers" "test" { + server_id = var.image_server_id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `region` - (Optional, String) Specifies the region in which to query the resource. + If omitted, the provider-level region will be used. + +* `name` - (Optional, String) Specified the name of the image server. + Fuzzy search is supported. + +* `server_id` - (Optional, String) Specified the ID of the image server. + +* `enterprise_project_id` - (Optional, String) Specifies the ID of the enterprise project to which the image server belongs. + This parameter is only valid for enterprise users, if omitted, all enterprise project IDs will be queried. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The data source ID. + +* `servers` - All image servers that match the filter parameters. + + The [servers](#app_image_servers) structure is documented below. + + +The `servers` block supports: + +* `id` - The ID of the image server. + +* `name` - The name of the image server. + +* `description` - The description of the image server. + +* `image_generated_product_id` - The ID of the generated image product. + +* `image_id` - The ID of the basic image to which the image server belongs. + +* `image_type` - The type of the basic image to which the image server belongs. + + **gold**: The market image. + + **public**: The public image. + + **private**: The private image. + + **shared**: The shared image. + + **other** + +* `spce_code` - The specification code of the basic image to which the image server belongs. + +* `aps_server_group_id` - The ID of the APS server group associated with the image server. + +* `aps_server_id` - The ID of the APS server associated with the image server. + +* `app_group_id` - The ID of the application group associated with the image server. + +* `status` - The current status of the image server. + + **ACTIVE** + + **BUILT**: Image task is finished. + + **ERROR** + +* `authorize_accounts` - The list of authorized users of the application group associated with the image server. + + The [authorize_accounts](#servers_authorize_accounts_struct) structure is documented below. + +* `enterprise_project_id` - The enterprise project ID to which the image server belongs. + +* `created_at` - The creation time of the image server, in RFC3339 format. + +* `updated_at` - The latest update time of the image server, in RFC3339 format. + + +The `authorize_accounts` block supports: + +* `account` - The name of the account. + +* `type` - The type of the account. + +* `domain` - The domain name of the Workspace service. diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index 0962e495f0..dd735a6463 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -1198,6 +1198,7 @@ func Provider() *schema.Provider { "huaweicloud_workspace_app_group_authorizations": workspace.DataSourceWorkspaceAppGroupAuthorizations(), "huaweicloud_workspace_app_groups": workspace.DataSourceWorkspaceAppGroups(), + "huaweicloud_workspace_app_image_servers": workspace.DataSourceWorkspaceAppImageServers(), "huaweicloud_workspace_app_nas_storages": workspace.DataSourceAppNasStorages(), "huaweicloud_workspace_app_publishable_apps": workspace.DataSourceWorkspaceAppPublishableApps(), "huaweicloud_workspace_app_storage_policies": workspace.DataSourceAppStoragePolicies(), diff --git a/huaweicloud/services/acceptance/workspace/data_source_huaweicloud_workspace_app_image_servers_test.go b/huaweicloud/services/acceptance/workspace/data_source_huaweicloud_workspace_app_image_servers_test.go new file mode 100644 index 0000000000..ac054e9aa4 --- /dev/null +++ b/huaweicloud/services/acceptance/workspace/data_source_huaweicloud_workspace_app_image_servers_test.go @@ -0,0 +1,130 @@ +package workspace + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance" +) + +func TestAccDataSourceAppImageServers_basic(t *testing.T) { + var ( + dataSource = "data.huaweicloud_workspace_app_image_servers.test" + rName = acceptance.RandomAccResourceName() + dc = acceptance.InitDataSourceCheck(dataSource) + + byServerId = "data.huaweicloud_workspace_app_image_servers.filter_by_server_id" + dcByServerId = acceptance.InitDataSourceCheck(byServerId) + + byName = "data.huaweicloud_workspace_app_image_servers.filter_by_name" + dcByName = acceptance.InitDataSourceCheck(byName) + + byEpsId = "data.huaweicloud_workspace_app_image_servers.filter_by_eps_id" + dcByEpsId = acceptance.InitDataSourceCheck(byEpsId) + ) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acceptance.TestAccPreCheck(t) + acceptance.TestAccPreCheckWorkspaceAppServerGroup(t) + acceptance.TestAccPreCheckWorkspaceAppImageSpecCode(t) + acceptance.TestAccPrecheckWorkspaceUserNames(t) + acceptance.TestAccPreCheckWorkspaceOUName(t) + acceptance.TestAccPreCheckWorkspaceADDomainNames(t) + }, + ProviderFactories: acceptance.TestAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testDataSourceAppImageServers_basic(rName), + Check: resource.ComposeTestCheckFunc( + dc.CheckResourceExists(), + resource.TestMatchResourceAttr(dataSource, "servers.#", regexp.MustCompile(`^[1-9]([0-9]*)?$`)), + dcByServerId.CheckResourceExists(), + resource.TestCheckResourceAttrSet(byServerId, "servers.0.image_id"), + resource.TestCheckResourceAttrSet(byServerId, "servers.0.image_type"), + resource.TestCheckResourceAttrSet(byServerId, "servers.0.spce_code"), + resource.TestCheckResourceAttrSet(byServerId, "servers.0.aps_server_group_id"), + resource.TestCheckResourceAttrSet(byServerId, "servers.0.aps_server_id"), + resource.TestCheckResourceAttrSet(byServerId, "servers.0.app_group_id"), + resource.TestCheckResourceAttrSet(byServerId, "servers.0.status"), + resource.TestCheckResourceAttr(byServerId, "servers.0.authorize_accounts.#", "1"), + resource.TestCheckResourceAttr(byServerId, "servers.0.authorize_accounts.0.type", "USER"), + resource.TestCheckResourceAttrSet(byServerId, "servers.0.authorize_accounts.0.domain"), + resource.TestCheckOutput("is_server_id_filter_useful", "true"), + resource.TestMatchResourceAttr(byServerId, "servers.0.created_at", + regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}?(Z|([+-]\d{2}:\d{2}))$`)), + resource.TestMatchResourceAttr(byServerId, "servers.0.updated_at", + regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}?(Z|([+-]\d{2}:\d{2}))$`)), + dcByName.CheckResourceExists(), + resource.TestCheckOutput("is_name_filter_useful", "true"), + dcByEpsId.CheckResourceExists(), + resource.TestCheckOutput("is_eps_id_filter_useful", "true"), + ), + }, + }, + }) +} + +func testDataSourceAppImageServers_basic(name string) string { + return fmt.Sprintf(` +%[1]s + +data "huaweicloud_workspace_app_image_servers" "test" { + depends_on = [huaweicloud_workspace_app_image_server.test] +} + +locals { + server_id = huaweicloud_workspace_app_image_server.test.id + server_name = huaweicloud_workspace_app_image_server.test.name + eps_id = huaweicloud_workspace_app_image_server.test.enterprise_project_id +} + +data "huaweicloud_workspace_app_image_servers" "filter_by_server_id" { + depends_on = [huaweicloud_workspace_app_image_server.test] + + server_id = local.server_id +} + +locals { + server_id_filter_result = [for v in data.huaweicloud_workspace_app_image_servers.filter_by_server_id.servers[*].id : + v == local.server_id] +} + +output "is_server_id_filter_useful" { + value = length(local.server_id_filter_result) == 1 && alltrue(local.server_id_filter_result) +} + +# Fuzzy search is supported. +data "huaweicloud_workspace_app_image_servers" "filter_by_name" { + depends_on = [huaweicloud_workspace_app_image_server.test] + + name = local.server_name +} + +locals { + name_filter_result = [for v in data.huaweicloud_workspace_app_image_servers.filter_by_name.servers : strcontains(v.name, local.server_name)] +} + +output "is_name_filter_useful" { + value = length(local.name_filter_result) > 0 && alltrue(local.name_filter_result) +} + +data "huaweicloud_workspace_app_image_servers" "filter_by_eps_id" { + depends_on = [huaweicloud_workspace_app_image_server.test] + + enterprise_project_id = local.eps_id +} + +locals { + eps_id_filter_result = [for v in data.huaweicloud_workspace_app_image_servers.filter_by_eps_id.servers[*].enterprise_project_id : + v == local.eps_id] +} + +output "is_eps_id_filter_useful" { + value = length(local.eps_id_filter_result) > 0 && alltrue(local.eps_id_filter_result) +} +`, testResourceAppImageServer_basic(name, "Data source test")) +} diff --git a/huaweicloud/services/acceptance/workspace/resource_huaweicloud_workspace_app_image_server_test.go b/huaweicloud/services/acceptance/workspace/resource_huaweicloud_workspace_app_image_server_test.go index 21aa1bf3b2..d95e127bde 100644 --- a/huaweicloud/services/acceptance/workspace/resource_huaweicloud_workspace_app_image_server_test.go +++ b/huaweicloud/services/acceptance/workspace/resource_huaweicloud_workspace_app_image_server_test.go @@ -123,7 +123,7 @@ resource "huaweicloud_workspace_app_image_server" "test" { authorize_accounts { account = split(",", "%[8]s")[0] type = "USER" - domain = element(split("%[9]s", ","), 0) + domain = element(split(",", "%[9]s"), 0) } root_volume { diff --git a/huaweicloud/services/workspace/data_source_huaweicloud_workspace_app_image_servers.go b/huaweicloud/services/workspace/data_source_huaweicloud_workspace_app_image_servers.go new file mode 100644 index 0000000000..01064ff91f --- /dev/null +++ b/huaweicloud/services/workspace/data_source_huaweicloud_workspace_app_image_servers.go @@ -0,0 +1,251 @@ +// Generated by PMS #445 +package workspace + +import ( + "context" + + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/go-uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/tidwall/gjson" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/helper/httphelper" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/helper/schemas" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils" +) + +func DataSourceWorkspaceAppImageServers() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceWorkspaceAppImageServersRead, + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: `Specifies the region in which to query the resource. If omitted, the provider-level region will be used.`, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Description: `Specified the name of the image server.`, + }, + "server_id": { + Type: schema.TypeString, + Optional: true, + Description: `Specified the ID of the image server.`, + }, + "enterprise_project_id": { + Type: schema.TypeString, + Optional: true, + Description: `Specifies the ID of the enterprise project to which the image server belong.`, + }, + "servers": { + Type: schema.TypeList, + Computed: true, + Description: `All image servers that match the filter parameters.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: `The ID of the image server.`, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: `The name of the image server.`, + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: `The description of the image server.`, + }, + "image_generated_product_id": { + Type: schema.TypeString, + Computed: true, + Description: `The ID of the generated image product.`, + }, + "image_id": { + Type: schema.TypeString, + Computed: true, + Description: `The ID of the basic image to which the image server belongs.`, + }, + "image_type": { + Type: schema.TypeString, + Computed: true, + Description: `The type of the basic image to which the image server belongs.`, + }, + "spce_code": { + Type: schema.TypeString, + Computed: true, + Description: `The specification code of the basic image to which the image server belongs.`, + }, + "aps_server_group_id": { + Type: schema.TypeString, + Computed: true, + Description: `The ID of the APS server group associated with the image server.`, + }, + "aps_server_id": { + Type: schema.TypeString, + Computed: true, + Description: `The ID of the APS server associated with the image server.`, + }, + "app_group_id": { + Type: schema.TypeString, + Computed: true, + Description: `The ID of the application group associated with the image server.`, + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: `The current status of the image server.`, + }, + "authorize_accounts": { + Type: schema.TypeList, + Computed: true, + Description: `The list of authorized users of the application group associated with the image server.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "account": { + Type: schema.TypeString, + Computed: true, + Description: `The name of the account.`, + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: `The type of the account.`, + }, + "domain": { + Type: schema.TypeString, + Computed: true, + Description: `The domain name of the Workspace service.`, + }, + }, + }, + }, + "enterprise_project_id": { + Type: schema.TypeString, + Computed: true, + Description: `The enterprise project ID to which the image server belongs.`, + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: `The creation time of the image server, in RFC3339 format.`, + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + Description: `The latest update time of the image server, in RFC3339 format.`, + }, + }, + }, + }, + }, + } +} + +type AppImageServersDSWrapper struct { + *schemas.ResourceDataWrapper + Config *config.Config +} + +func newAppImageServersDSWrapper(d *schema.ResourceData, meta interface{}) *AppImageServersDSWrapper { + return &AppImageServersDSWrapper{ + ResourceDataWrapper: schemas.NewSchemaWrapper(d), + Config: meta.(*config.Config), + } +} + +func dataSourceWorkspaceAppImageServersRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + wrapper := newAppImageServersDSWrapper(d, meta) + listImageServersRst, err := wrapper.ListImageServers() + if err != nil { + return diag.FromErr(err) + } + + id, err := uuid.GenerateUUID() + if err != nil { + return diag.FromErr(err) + } + d.SetId(id) + + err = wrapper.listImageServersToSchema(listImageServersRst) + if err != nil { + return diag.FromErr(err) + } + + return nil +} + +// @API Workspace GET /v1/{project_id}/image-servers +func (w *AppImageServersDSWrapper) ListImageServers() (*gjson.Result, error) { + client, err := w.NewClient(w.Config, "appstream") + if err != nil { + return nil, err + } + + uri := "/v1/{project_id}/image-servers" + params := map[string]any{ + "server_name": w.Get("name"), + "server_id": w.Get("server_id"), + "enterprise_project_id": w.Get("enterprise_project_id"), + } + params = utils.RemoveNil(params) + return httphelper.New(client). + Method("GET"). + URI(uri). + Query(params). + OffsetPager("items", "offset", "limit", 100). + Request(). + Result() +} + +func (w *AppImageServersDSWrapper) listImageServersToSchema(body *gjson.Result) error { + d := w.ResourceData + mErr := multierror.Append(nil, + d.Set("region", w.Config.GetRegion(w.ResourceData)), + d.Set("servers", schemas.SliceToList(body.Get("items"), + func(servers gjson.Result) any { + return map[string]any{ + "image_id": servers.Get("image_ref.id").Value(), + "image_type": servers.Get("image_ref.image_type").Value(), + "spce_code": servers.Get("image_ref.spce_code").Value(), + "id": servers.Get("id").Value(), + "name": servers.Get("name").Value(), + "description": servers.Get("description").Value(), + "image_generated_product_id": servers.Get("image_id").Value(), + "aps_server_group_id": servers.Get("server_group_id").Value(), + "aps_server_id": servers.Get("server_id").Value(), + "app_group_id": servers.Get("app_group_id").Value(), + "status": servers.Get("status").Value(), + "authorize_accounts": schemas.SliceToList(servers.Get("authorize_accounts"), + func(authorizeAccounts gjson.Result) any { + return map[string]any{ + "account": authorizeAccounts.Get("account").Value(), + "type": authorizeAccounts.Get("account_type").Value(), + "domain": authorizeAccounts.Get("domain").Value(), + } + }, + ), + "enterprise_project_id": servers.Get("enterprise_project_id").Value(), + "created_at": w.setItemsCreateTime(servers), + "updated_at": w.setItemsUpdateTime(servers), + } + }, + )), + ) + return mErr.ErrorOrNil() +} + +func (*AppImageServersDSWrapper) setItemsCreateTime(data gjson.Result) string { + return utils.FormatTimeStampRFC3339(utils.ConvertTimeStrToNanoTimestamp(data.Get("create_time").String())/1000, false) +} + +func (*AppImageServersDSWrapper) setItemsUpdateTime(data gjson.Result) string { + return utils.FormatTimeStampRFC3339(utils.ConvertTimeStrToNanoTimestamp(data.Get("update_time").String())/1000, false) +}