From a41110712ed7f7cc61217414cc9e551215af547e Mon Sep 17 00:00:00 2001 From: saf3dfsa Date: Wed, 18 Dec 2024 11:13:26 +0800 Subject: [PATCH] feat(CodeArts/Deploy): support group permission management --- docs/resources/codearts_deploy_group.md | 28 ++++ .../codearts_deploy_group_permission.md | 58 +++++++ huaweicloud/provider.go | 7 +- ...d_codearts_deploy_group_permission_test.go | 37 +++++ ...ource_huaweicloud_codearts_deploy_group.go | 124 ++++++++++++++ ...icloud_codearts_deploy_group_permission.go | 153 ++++++++++++++++++ 6 files changed, 404 insertions(+), 3 deletions(-) create mode 100644 docs/resources/codearts_deploy_group_permission.md create mode 100644 huaweicloud/services/acceptance/codeartsdeploy/resource_huaweicloud_codearts_deploy_group_permission_test.go create mode 100644 huaweicloud/services/codeartsdeploy/resource_huaweicloud_codearts_deploy_group_permission.go diff --git a/docs/resources/codearts_deploy_group.md b/docs/resources/codearts_deploy_group.md index b7ff69238e0..1efa1d8b22a 100644 --- a/docs/resources/codearts_deploy_group.md +++ b/docs/resources/codearts_deploy_group.md @@ -93,6 +93,9 @@ In addition to all arguments above, the following attributes are exported: * `permission` - The group permission detail. The [permission](#DeployGroup_permission) structure is documented below. +* `permission_matrix` - The group permission matrix detail. + The [permission_matrix](#DeployGroup_permission_matrix) structure is documented below. + The `object` block supports: @@ -115,6 +118,31 @@ The `permission` block supports: * `can_copy` - Indicates whether the user has the permission to copy. + +The `permission_matrix` block supports: + +* `role_id` - Indicates the role ID. + +* `role_name` - Indicates the role name. + +* `role_type` - Indicates the role type. + +* `can_view` - Indicates whether the role has the view permission. + +* `can_edit` - Indicates whether the role has the edit permission. + +* `can_delete` - Indicates whether the role has the deletion permission. + +* `can_add_host` - Indicates whether the role has the permission to add hosts. + +* `can_manage` - Indicates whether the role has the management permission. + +* `can_copy` - Indicates whether the role has the permission to copy. + +* `created_at` - The permission create time. + +* `updated_at` - The permission update time. + ## Import The CodeArts deploy group resource can be imported using the `project_id` and `id`, separated by a slash, e.g. diff --git a/docs/resources/codearts_deploy_group_permission.md b/docs/resources/codearts_deploy_group_permission.md new file mode 100644 index 00000000000..a2b0c57a8a5 --- /dev/null +++ b/docs/resources/codearts_deploy_group_permission.md @@ -0,0 +1,58 @@ +--- +subcategory: "CodeArts Deploy" +layout: "huaweicloud" +page_title: "HuaweiCloud: huaweicloud_codearts_deploy_group_permission" +description: |- + Manages a CodeArts deploy group permission resource within HuaweiCloud. +--- + +# huaweicloud_codearts_deploy_group_permission + +Manages a CodeArts deploy group permission resource within HuaweiCloud. + +## Example Usage + +```hcl +variable "project_id" {} +variable "group_id" {} +variable "role_id" {} + +resource "huaweicloud_codearts_deploy_group_permission" "test" { + project_id = var.project_id + group_id = var.group_id + role_id = var.role_id + permission_name = "can_add_host" + permission_value = false +} +``` + +## 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 creates a new resource. + +* `project_id` - (Required, String, ForceNew) Specifies the project ID. + Changing this creates a new resource. + +* `group_id` - (Required, String, ForceNew) Specifies the group ID. + Changing this creates a new resource. + +* `role_id` - (Required, String, ForceNew) Specifies the role ID. + Changing this creates a new resource. + +* `permission_name` - (Required, String, ForceNew) Specifies the permission name. + Valid values are **can_view**, **can_edit**, **can_delete**, **can_add_host**, **can_manage**, and **can_copy**. + + Changing this creates a new resource. + +* `permission_value` - (Optional, Bool, ForceNew) Specifies whether to enable the permission. + Changing this creates a new resource. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The resource ID. diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index 31d9a824859..2f9785dbd8b 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -2191,9 +2191,10 @@ func Provider() *schema.Provider { "huaweicloud_codearts_project": codearts.ResourceProject(), "huaweicloud_codearts_repository": codearts.ResourceRepository(), - "huaweicloud_codearts_deploy_application": codeartsdeploy.ResourceDeployApplication(), - "huaweicloud_codearts_deploy_group": codeartsdeploy.ResourceDeployGroup(), - "huaweicloud_codearts_deploy_host": codeartsdeploy.ResourceDeployHost(), + "huaweicloud_codearts_deploy_application": codeartsdeploy.ResourceDeployApplication(), + "huaweicloud_codearts_deploy_group": codeartsdeploy.ResourceDeployGroup(), + "huaweicloud_codearts_deploy_group_permission": codeartsdeploy.ResourceDeployGroupPermission(), + "huaweicloud_codearts_deploy_host": codeartsdeploy.ResourceDeployHost(), "huaweicloud_codearts_inspector_website": codeartsinspector.ResourceInspectorWebsite(), "huaweicloud_codearts_inspector_website_scan": codeartsinspector.ResourceInspectorWebsiteScan(), diff --git a/huaweicloud/services/acceptance/codeartsdeploy/resource_huaweicloud_codearts_deploy_group_permission_test.go b/huaweicloud/services/acceptance/codeartsdeploy/resource_huaweicloud_codearts_deploy_group_permission_test.go new file mode 100644 index 00000000000..9fa5167dcec --- /dev/null +++ b/huaweicloud/services/acceptance/codeartsdeploy/resource_huaweicloud_codearts_deploy_group_permission_test.go @@ -0,0 +1,37 @@ +package codeartsdeploy + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance" +) + +func TestAccDeployGroupPermissionModify_basic(t *testing.T) { + rName := acceptance.RandomAccResourceName() + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t) }, + ProviderFactories: acceptance.TestAccProviderFactories, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccDeployGroupPermissionModify_basic(rName), + }, + }, + }) +} + +func testAccDeployGroupPermissionModify_basic(rName string) string { + return fmt.Sprintf(` +%s + +resource "huaweicloud_codearts_deploy_group_permission" "test" { + project_id = huaweicloud_codearts_deploy_group.test.project_id + group_id = huaweicloud_codearts_deploy_group.test.id + role_id = try(huaweicloud_codearts_deploy_group.test.permission_matrix[2].role_id, "") + permission_name = "can_add_host" + permission_value = false +}`, testDeployGroup_basic(rName)) +} diff --git a/huaweicloud/services/codeartsdeploy/resource_huaweicloud_codearts_deploy_group.go b/huaweicloud/services/codeartsdeploy/resource_huaweicloud_codearts_deploy_group.go index 7e2ce340df4..83c52a4b433 100644 --- a/huaweicloud/services/codeartsdeploy/resource_huaweicloud_codearts_deploy_group.go +++ b/huaweicloud/services/codeartsdeploy/resource_huaweicloud_codearts_deploy_group.go @@ -21,6 +21,7 @@ import ( // @API CodeArtsDeploy PUT /v1/resources/host-groups/{group_id} // @API CodeArtsDeploy GET /v1/resources/host-groups/{group_id} // @API CodeArtsDeploy DELETE /v1/resources/host-groups/{group_id} +// @API CodeArtsDeploy GET /v2/host-groups/{group_id}/permissions func ResourceDeployGroup() *schema.Resource { return &schema.Resource{ CreateContext: resourceDeployGroupCreate, @@ -92,6 +93,11 @@ func ResourceDeployGroup() *schema.Resource { Elem: deployGroupPermissionSchema(), Computed: true, }, + "permission_matrix": { + Type: schema.TypeList, + Elem: deployGroupPermissionMatrixSchema(), + Computed: true, + }, }, } } @@ -152,6 +158,69 @@ func deployGroupPermissionSchema() *schema.Resource { return &sc } +func deployGroupPermissionMatrixSchema() *schema.Resource { + sc := schema.Resource{ + Schema: map[string]*schema.Schema{ + "role_id": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the role ID.`, + }, + "role_name": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the role name.`, + }, + "role_type": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the role type.`, + }, + "can_view": { + Type: schema.TypeBool, + Computed: true, + Description: `Indicates whether the role has the view permission.`, + }, + "can_edit": { + Type: schema.TypeBool, + Computed: true, + Description: `Indicates whether the role has the edit permission.`, + }, + "can_delete": { + Type: schema.TypeBool, + Computed: true, + Description: `Indicates whether the role has the deletion permission.`, + }, + "can_add_host": { + Type: schema.TypeBool, + Computed: true, + Description: `Indicates whether the role has the permission to add hosts.`, + }, + "can_manage": { + Type: schema.TypeBool, + Computed: true, + Description: `Indicates whether the role has the management permission.`, + }, + "can_copy": { + Type: schema.TypeBool, + Computed: true, + Description: `Indicates whether the role has the permission to copy.`, + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: `The create time.`, + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + Description: `The update time.`, + }, + }, + } + return &sc +} + func resourceDeployGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var ( cfg = meta.(*config.Config) @@ -236,6 +305,11 @@ func resourceDeployGroupRead(_ context.Context, d *schema.ResourceData, meta int return diag.Errorf("error retrieving CodeArts deploy group: result is not found in API response") } + permissionMatrix, err := getDeployGroupPermissionMatrix(client, d.Id()) + if err != nil { + return diag.FromErr(err) + } + mErr = multierror.Append( mErr, d.Set("region", region), @@ -248,11 +322,40 @@ func resourceDeployGroupRead(_ context.Context, d *schema.ResourceData, meta int d.Set("updated_at", utils.PathSearch("updated_time", resultRespBody, nil)), d.Set("created_by", flattenDeployGroupCreatedBy(resultRespBody)), d.Set("permission", flattenDeployGroupPermission(resultRespBody)), + d.Set("permission_matrix", flattenDeployGroupPermissionMatrix(permissionMatrix.([]interface{}))), ) return diag.FromErr(mErr.ErrorOrNil()) } +func getDeployGroupPermissionMatrix(client *golangsdk.ServiceClient, id string) (interface{}, error) { + httpUrl := "v2/host-groups/{group_id}/permissions" + getPath := client.Endpoint + httpUrl + getPath = strings.ReplaceAll(getPath, "{group_id}", id) + getOpt := golangsdk.RequestOpts{ + KeepResponseBody: true, + } + + getResp, err := client.Request("GET", getPath, &getOpt) + if err != nil { + return nil, err + } + getRespBody, err := utils.FlattenResponse(getResp) + if err != nil { + return nil, err + } + + permissionMatrix := getRespBody.([]interface{}) + if len(permissionMatrix) == 0 { + return nil, golangsdk.ErrDefault404{ + ErrUnexpectedResponseCode: golangsdk.ErrUnexpectedResponseCode{ + Body: []byte("error retrieving CodeArts deploy group permission matrix, empty list"), + }, + } + } + return permissionMatrix, nil +} + func flattenDeployGroupCreatedBy(resp interface{}) []interface{} { curJson := utils.PathSearch("created_by", resp, nil) if curJson == nil { @@ -287,6 +390,27 @@ func flattenDeployGroupPermission(resp interface{}) []interface{} { } } +func flattenDeployGroupPermissionMatrix(resp []interface{}) []interface{} { + rst := make([]interface{}, 0, len(resp)) + for _, v := range resp { + rst = append(rst, map[string]interface{}{ + "role_id": utils.PathSearch("role_id", v, nil), + "role_name": utils.PathSearch("name", v, nil), + "role_type": utils.PathSearch("role_type", v, nil), + "can_view": utils.PathSearch("can_view", v, nil), + "can_edit": utils.PathSearch("can_edit", v, nil), + "can_delete": utils.PathSearch("can_delete", v, nil), + "can_add_host": utils.PathSearch("can_add_host", v, nil), + "can_manage": utils.PathSearch("can_manage", v, nil), + "can_copy": utils.PathSearch("can_copy", v, nil), + "created_at": utils.PathSearch("create_time", v, nil), + "updated_at": utils.PathSearch("update_time", v, nil), + }) + } + + return rst +} + func resourceDeployGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var ( cfg = meta.(*config.Config) diff --git a/huaweicloud/services/codeartsdeploy/resource_huaweicloud_codearts_deploy_group_permission.go b/huaweicloud/services/codeartsdeploy/resource_huaweicloud_codearts_deploy_group_permission.go new file mode 100644 index 00000000000..64e5b36a0d3 --- /dev/null +++ b/huaweicloud/services/codeartsdeploy/resource_huaweicloud_codearts_deploy_group_permission.go @@ -0,0 +1,153 @@ +package codeartsdeploy + +import ( + "context" + "fmt" + "strings" + + "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/chnsz/golangsdk" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/common" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils" +) + +// @API CodeArtsDeploy PUT /v2/host-groups/{group_id}/permissions +// @API CodeArtsDeploy GET /v2/host-groups/{group_id}/permissions +func ResourceDeployGroupPermission() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceDeployGroupPermissionCreate, + ReadContext: resourceDeployGroupPermissionRead, + DeleteContext: resourceDeployGroupPermissionDelete, + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "project_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "group_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "role_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "permission_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "permission_value": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceDeployGroupPermissionCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + client, err := cfg.NewServiceClient("codearts_deploy", cfg.GetRegion(d)) + if err != nil { + return diag.Errorf("error creating CodeArts deploy client: %s", err) + } + + err = modifyDeployGroupPermission(client, d) + if err != nil { + return diag.FromErr(err) + } + + id, err := uuid.GenerateUUID() + if err != nil { + return diag.Errorf("unable to generate ID: %s", err) + } + d.SetId(id) + + return resourceDeployGroupPermissionRead(ctx, d, meta) +} + +func modifyDeployGroupPermission(client *golangsdk.ServiceClient, d *schema.ResourceData) error { + httpUrl := "v2/host-groups/{group_id}/permissions" + modifyPath := client.Endpoint + httpUrl + modifyPath = strings.ReplaceAll(modifyPath, "{group_id}", d.Get("group_id").(string)) + + modifyOpt := golangsdk.RequestOpts{ + KeepResponseBody: true, + MoreHeaders: map[string]string{ + "Content-Type": "application/json;charset=utf-8", + }, + JSONBody: buildDeployGroupPermissionBodyParams(d), + } + + _, err := client.Request("PUT", modifyPath, &modifyOpt) + if err != nil { + return fmt.Errorf("error modifying CodeArts deploy group permission") + } + + return nil +} + +func buildDeployGroupPermissionBodyParams(d *schema.ResourceData) map[string]interface{} { + return map[string]interface{}{ + "project_id": d.Get("project_id"), + "role_id": d.Get("role_id"), + "permission_name": d.Get("permission_name"), + "permission_value": d.Get("permission_value"), + } +} + +func resourceDeployGroupPermissionRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + region := cfg.GetRegion(d) + client, err := cfg.NewServiceClient("codearts_deploy", region) + if err != nil { + return diag.Errorf("error creating CodeArts deploy client: %s", err) + } + + permissionMatrix, err := getDeployGroupPermissionMatrix(client, d.Get("group_id").(string)) + if err != nil { + return common.CheckDeletedDiag(d, err, "error retrieving CodeArts deploy group permission") + } + + roleId := d.Get("role_id").(string) + permissionName := d.Get("permission_name").(string) + expression := fmt.Sprintf("[?role_id=='%s']|[0]", roleId) + role := utils.PathSearch(expression, permissionMatrix, nil) + if role == nil { + return diag.Errorf("unable to find role (%s) from API response", roleId) + } + + mErr := multierror.Append(nil, + d.Set("region", region), + d.Set("permission_value", utils.PathSearch(permissionName, role, nil)), + ) + + return diag.FromErr(mErr.ErrorOrNil()) +} + +func resourceDeployGroupPermissionDelete(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + errorMsg := "Deleting permission resource is not supported. The resource is only removed from the state," + + " the group permission matrix remains in the cloud." + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: errorMsg, + }, + } +}