Skip to content

Commit

Permalink
feat(rms): RMS organizational assignment package supports updates
Browse files Browse the repository at this point in the history
  • Loading branch information
profoundwu committed Dec 27, 2024
1 parent bbac193 commit b1fa2cc
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 30 deletions.
25 changes: 10 additions & 15 deletions docs/resources/rms_organizational_assignment_package.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,38 +39,33 @@ The following arguments are supported:

Changing this parameter will create a new resource.

* `name` - (Required, String, ForceNew) Specifies the assignment package name. It contains 1 to 64 characters.
* `name` - (Required, String) Specifies the assignment package name. It contains `1` to `64` characters.

Changing this parameter will create a new resource.
* `excluded_accounts` - (Optional, List) Specifies the excluded accounts for conformance package deployment.

* `excluded_accounts` - (Optional, List, ForceNew) Specifies the excluded accounts for conformance package deployment.
* `template_key` - (Optional, String, ForceNew) Specifies the name of a predefined conformance package. It contains `1` to
`128` characters.

Changing this parameter will create a new resource.

* `template_key` - (Optional, String, ForceNew) Specifies the name of a predefined conformance package. It contains 1 to
128 characters.
* `template_body` - (Optional, String, ForceNew) Specifies the content of a custom assignment package. It contains `1` to
`51200` characters.

Changing this parameter will create a new resource.

* `template_body` - (Optional, String, ForceNew) Specifies the content of a custom assignment package. It contains 1 to
51200 characters.
* `template_uri` - (Optional, String, ForceNew) Specifies the OBS address of a conformance package. It contains `1` to
`1024` characters.

Changing this parameter will create a new resource.

* `template_uri` - (Optional, String, ForceNew) Specifies the OBS address of a conformance package. It contains 1 to
1024 characters.

Changing this parameter will create a new resource.
* `vars_structure` - (Optional, List) Specifies the parameters of a conformance package.

* `vars_structure` - (Optional, List, ForceNew) Specifies the parameters of a conformance package.

Changing this parameter will create a new resource.
The [vars_structure](#OrgAssignmentPackage_VarStructure) structure is documented below.

<a name="OrgAssignmentPackage_VarStructure"></a>
The `vars_structure` block supports:

* `var_key` - (Optional, String) Specifies the name of a parameter. It contains 1 to 64 characters.
* `var_key` - (Optional, String) Specifies the name of a parameter. It contains `1` to `64` characters.

* `var_value` - (Optional, String) Specifies the value of a parameter. It's a json string.

Expand Down
11 changes: 10 additions & 1 deletion huaweicloud/services/acceptance/acceptance.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ var (
HW_RAM_SHARE_INVITATION_ID = os.Getenv("HW_RAM_SHARE_INVITATION_ID")
HW_RAM_SHARE_ID = os.Getenv("HW_RAM_SHARE_ID")

HW_RMS_TARGET_ID = os.Getenv("HW_RMS_TARGET_ID")
HW_RMS_TARGET_ID = os.Getenv("HW_RMS_TARGET_ID")
HW_RMS_EXCLUDED_ACCOUNT_1 = os.Getenv("HW_RMS_EXCLUDED_ACCOUNT_1")
HW_RMS_EXCLUDED_ACCOUNT_2 = os.Getenv("HW_RMS_EXCLUDED_ACCOUNT_2")

HW_CDN_DOMAIN_NAME = os.Getenv("HW_CDN_DOMAIN_NAME")
// `HW_CDN_CERT_DOMAIN_NAME` Configure the domain name environment variable of the certificate type.
Expand Down Expand Up @@ -1135,6 +1137,13 @@ func TestAccPreCheckRMSTargetID(t *testing.T) {
}
}

// lintignore:AT003
func TestAccPreCheckRMSExcludedAccounts(t *testing.T) {
if HW_RMS_EXCLUDED_ACCOUNT_1 == "" || HW_RMS_EXCLUDED_ACCOUNT_2 == "" {
t.Skip("HW_RMS_EXCLUDED_ACCOUNT_1 and HW_RMS_EXCLUDED_ACCOUNT_2 must be set for the acceptance tests.")
}
}

// lintignore:AT003
func TestAccPreCheckDms(t *testing.T) {
if HW_DMS_ENVIRONMENT == "" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func TestAccOrgAssignmentPackage_basic(t *testing.T) {
PreCheck: func() {
acceptance.TestAccPreCheck(t)
acceptance.TestAccPreCheckOrganizationsOpen(t)
acceptance.TestAccPreCheckRMSExcludedAccounts(t)
},
ProviderFactories: acceptance.TestAccProviderFactories,
CheckDestroy: rc.CheckResourceDestroy(),
Expand All @@ -85,6 +86,23 @@ func TestAccOrgAssignmentPackage_basic(t *testing.T) {
resource.TestCheckResourceAttrSet(rName, "updated_at"),
),
},
{
Config: testOrgAssignmentPackage_update(name),
Check: resource.ComposeTestCheckFunc(
rc.CheckResourceExists(),
resource.TestCheckResourceAttrPair(rName, "organization_id",
"data.huaweicloud_organizations_organization.test", "id"),
resource.TestCheckResourceAttr(rName, "name", name+"-update"),
resource.TestCheckTypeSetElemNestedAttrs(rName, "vars_structure.*", map[string]string{
"var_key": "lastBackupAgeValue",
"var_value": "25",
}),
resource.TestCheckResourceAttrSet(rName, "owner_id"),
resource.TestCheckResourceAttrSet(rName, "org_conformance_pack_urn"),
resource.TestCheckResourceAttrSet(rName, "created_at"),
resource.TestCheckResourceAttrSet(rName, "updated_at"),
),
},
{
ResourceName: rName,
ImportState: true,
Expand All @@ -100,13 +118,20 @@ func testOrgAssignmentPackage_basic(name string) string {
return fmt.Sprintf(`
data "huaweicloud_organizations_organization" "test" {}
data "huaweicloud_rms_assignment_package_templates" "test" {}
data "huaweicloud_rms_assignment_package_templates" "test" {
template_key = "Operational-Best-Practices-for-ECS.tf.json"
}
resource "huaweicloud_rms_organizational_assignment_package" "test" {
organization_id = data.huaweicloud_organizations_organization.test.id
name = "%s"
name = "%[1]s"
template_key = data.huaweicloud_rms_assignment_package_templates.test.templates.0.template_key
excluded_accounts = [
"%[2]s",
"%[3]s",
]
dynamic "vars_structure" {
for_each = data.huaweicloud_rms_assignment_package_templates.test.templates.0.parameters
content {
Expand All @@ -115,7 +140,36 @@ resource "huaweicloud_rms_organizational_assignment_package" "test" {
}
}
}
`, name)
`, name, acceptance.HW_RMS_EXCLUDED_ACCOUNT_1, acceptance.HW_RMS_EXCLUDED_ACCOUNT_2)
}

func testOrgAssignmentPackage_update(name string) string {
return fmt.Sprintf(`
data "huaweicloud_organizations_organization" "test" {}
data "huaweicloud_rms_assignment_package_templates" "test" {
template_key = "Operational-Best-Practices-for-ECS.tf.json"
}
resource "huaweicloud_rms_organizational_assignment_package" "test" {
organization_id = data.huaweicloud_organizations_organization.test.id
name = "%[1]s-update"
template_key = data.huaweicloud_rms_assignment_package_templates.test.templates.0.template_key
excluded_accounts = [
"%[2]s",
"%[3]s",
]
dynamic "vars_structure" {
for_each = data.huaweicloud_rms_assignment_package_templates.test.templates.0.parameters
content {
var_key = vars_structure.value["name"]
var_value = vars_structure.value["name"] == "lastBackupAgeValue" ? 25 : vars_structure.value["default_value"]
}
}
}
`, name, acceptance.HW_RMS_EXCLUDED_ACCOUNT_1, acceptance.HW_RMS_EXCLUDED_ACCOUNT_2)
}

func testOrgAssignmentPackageImportState(name string) resource.ImportStateIdFunc {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,23 @@ import (
)

// @API Config POST /v1/resource-manager/organizations/{organization_id}/conformance-packs
// @API Config GET /v1/resource-manager/organizations/{organization_id}/conformance-packs/detailed-statuses
// @API Config GET /v1/resource-manager/organizations/{organization_id}/conformance-packs/statuses
// @API Config GET /v1/resource-manager/organizations/{organization_id}/conformance-packs/{conformance_pack_id}
// @API Config DELETE /v1/resource-manager/organizations/{organization_id}/conformance-packs/{conformance_pack_id}
// @API Config PUT /v1/resource-manager/organizations/{organization_id}/conformance-packs/{conformance_pack_id}
func ResourceOrgAssignmentPackage() *schema.Resource {
return &schema.Resource{
CreateContext: resourceOrgAssignmentPackageCreate,
ReadContext: resourceOrgAssignmentPackageRead,
UpdateContext: resourceOrgAssignmentPackageUpdate,
DeleteContext: resourceOrgAssignmentPackageDelete,
Importer: &schema.ResourceImporter{
StateContext: resourceOrgAssignmentPackageImportState,
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(30 * time.Minute),
Update: schema.DefaultTimeout(30 * time.Minute),
Delete: schema.DefaultTimeout(30 * time.Minute),
},

Expand All @@ -52,15 +55,13 @@ func ResourceOrgAssignmentPackage() *schema.Resource {
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: `Specifies the assignment package name.`,
},
"excluded_accounts": {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
Computed: true,
ForceNew: true,
Description: `Specifies the excluded accounts for conformance package deployment.`,
},
"template_key": {
Expand Down Expand Up @@ -90,7 +91,6 @@ func ResourceOrgAssignmentPackage() *schema.Resource {
Elem: orgAssignmentPackageVarStructureSchema(),
Optional: true,
Computed: true,
ForceNew: true,
Description: `Specifies the parameters of a conformance package.`,
},
"owner_id": {
Expand Down Expand Up @@ -192,15 +192,15 @@ func resourceOrgAssignmentPackageCreate(ctx context.Context, d *schema.ResourceD
}

if _, err = stateConf.WaitForStateContext(ctx); err != nil {
return diag.Errorf("error waiting for RMS organizational assignment Package (%s) to be created: %s",
return diag.Errorf("error waiting for RMS organizational assignment package (%s) to be created: %s",
id, err)
}

return resourceOrgAssignmentPackageRead(ctx, d, meta)
}

func buildCreateOrgAssignmentPackageBodyParams(d *schema.ResourceData) (map[string]interface{}, error) {
varsStructure, err := buildCreateOrgAssignmentPackageRequestBodyVarStructure(d.Get("vars_structure"))
varsStructure, err := buildOrgAssignmentPackageRequestBodyVarStructure(d.Get("vars_structure"))
if err != nil {
return nil, err
}
Expand All @@ -216,7 +216,7 @@ func buildCreateOrgAssignmentPackageBodyParams(d *schema.ResourceData) (map[stri
return bodyParams, nil
}

func buildCreateOrgAssignmentPackageRequestBodyVarStructure(rawParams interface{}) ([]map[string]interface{}, error) {
func buildOrgAssignmentPackageRequestBodyVarStructure(rawParams interface{}) ([]map[string]interface{}, error) {
rawArray := rawParams.(*schema.Set).List()
if len(rawArray) == 0 {
return nil, nil
Expand Down Expand Up @@ -307,6 +307,73 @@ func flattenGetOrgAssignmentPackageResponseBodyVarStructure(resp interface{}) []
return rst
}

func resourceOrgAssignmentPackageUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
cfg := meta.(*config.Config)
region := cfg.GetRegion(d)

// updateOrgAssignmentPackage: Update an existing RMS organizational assignment package
var (
updateOrgAssignmentPackageHttpUrl = "v1/resource-manager/organizations/{organization_id}/conformance-packs/{conformance_pack_id}"
updateOrgAssignmentPackageProduct = "rms"
conformancePackId = d.Id()
)
updateOrgAssignmentPackageClient, err := cfg.NewServiceClient(updateOrgAssignmentPackageProduct, region)
if err != nil {
return diag.Errorf("error creating Config client: %s", err)
}

updateOrgAssignmentPackagePath := updateOrgAssignmentPackageClient.Endpoint + updateOrgAssignmentPackageHttpUrl
updateOrgAssignmentPackagePath = strings.ReplaceAll(updateOrgAssignmentPackagePath, "{organization_id}",
d.Get("organization_id").(string))
updateOrgAssignmentPackagePath = strings.ReplaceAll(updateOrgAssignmentPackagePath, "{conformance_pack_id}", conformancePackId)

updateOrgAssignmentPackageOpt := golangsdk.RequestOpts{
KeepResponseBody: true,
}

updateOpts, err := buildUpdateOrgAssignmentPackageBodyParams(d)
if err != nil {
return diag.FromErr(err)
}

updateOrgAssignmentPackageOpt.JSONBody = utils.RemoveNil(updateOpts)
_, err = updateOrgAssignmentPackageClient.Request("PUT",
updateOrgAssignmentPackagePath, &updateOrgAssignmentPackageOpt)
if err != nil {
return diag.Errorf("error updating RMS organizational assignment package: %s", err)
}

stateConf := &resource.StateChangeConf{
Target: []string{"UPDATE_SUCCESSFUL", "ROLLBACK_SUCCESSFUL"},
Pending: []string{"UPDATE_IN_PROGRESS"},
Refresh: refreshDeployStatus(d, updateOrgAssignmentPackageClient),
Timeout: d.Timeout(schema.TimeoutUpdate),
Delay: 10 * time.Second,
PollInterval: 10 * time.Second,
}

if _, err = stateConf.WaitForStateContext(ctx); err != nil {
return diag.Errorf("error waiting for RMS organizational assignment package (%s) to be updated: %s",
conformancePackId, err)
}

return resourceOrgAssignmentPackageRead(ctx, d, meta)
}

func buildUpdateOrgAssignmentPackageBodyParams(d *schema.ResourceData) (map[string]interface{}, error) {
varsStructure, err := buildOrgAssignmentPackageRequestBodyVarStructure(d.Get("vars_structure"))
if err != nil {
return nil, err
}

bodyParams := map[string]interface{}{
"name": d.Get("name"),
"excluded_accounts": utils.ValueIgnoreEmpty(d.Get("excluded_accounts")),
"vars_structure": varsStructure,
}
return bodyParams, nil
}

func resourceOrgAssignmentPackageDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
cfg := meta.(*config.Config)
region := cfg.GetRegion(d)
Expand Down Expand Up @@ -346,7 +413,7 @@ func resourceOrgAssignmentPackageDelete(ctx context.Context, d *schema.ResourceD
}

if _, err = stateConf.WaitForStateContext(ctx); err != nil {
return diag.Errorf("error waiting for RMS organizational assignment Package (%s) to be deleted: %s", d.Id(), err)
return diag.Errorf("error waiting for RMS organizational assignment package (%s) to be deleted: %s", d.Id(), err)
}

return nil
Expand All @@ -356,7 +423,7 @@ func refreshDeployStatus(d *schema.ResourceData, client *golangsdk.ServiceClient
return func() (result interface{}, state string, err error) {
// getDeployStatus: Query the RMS organizational assignment package
var (
getDeployStatusHttpUrl = "v1/resource-manager/organizations/{organization_id}/conformance-packs/detailed-statuses"
getDeployStatusHttpUrl = "v1/resource-manager/organizations/{organization_id}/conformance-packs/statuses"
)

getDeployStatusPath := client.Endpoint + getDeployStatusHttpUrl
Expand All @@ -378,8 +445,7 @@ func refreshDeployStatus(d *schema.ResourceData, client *golangsdk.ServiceClient
if err != nil {
return nil, "", err
}
return getDeployStatusRespBody, utils.PathSearch(fmt.Sprintf("statuses[?conformance_pack_name=='%s']|[0].state",
conformancePackName), getDeployStatusRespBody, "").(string), nil
return getDeployStatusRespBody, utils.PathSearch("statuses|[0].state", getDeployStatusRespBody, "").(string), nil
}
}

Expand Down

0 comments on commit b1fa2cc

Please sign in to comment.