Skip to content

Commit c7e313b

Browse files
feat(dlc): [136138650] add tencentcloud_dlc_attach_user_policy_attachment resource (#4303)
* feat(dlc): add tencentcloud_dlc_attach_user_policyr_attachment resource * add * add * add --------- Co-authored-by: SevenEarth <391613297@qq.com>
1 parent 0552fb1 commit c7e313b

14 files changed

Lines changed: 1737 additions & 0 deletions

File tree

.changelog/4303.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:new-resource
2+
tencentcloud_dlc_attach_user_policyr_attachment
3+
```
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
schema: spec-driven
2+
created: 2026-07-14
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
## Context
2+
3+
The Terraform Provider for TencentCloud manages cloud resources via the `tencentcloud-sdk-go` SDK. The DLC (Data Lake Compute) product exposes APIs to bind (`AttachUserPolicy`) and unbind (`DetachUserPolicy`) authorization policies to/from sub-users, and to query a user's detailed info (`DescribeUserInfo`). Currently there is no Terraform resource representing this binding relationship, so users cannot manage DLC user-policy bindings as code.
4+
5+
The binding is described by two entities: a user (identified by `user_id`) and a set of policies (`policy_set`). The cloud APIs do not return a single stable identifier for a binding; instead the binding is identified by the combination of `user_id` and `account_type`. The cloud APIs are synchronous (no async polling is required per the API descriptions and the `AttachUserPolicy`/`DetachUserPolicy` responses contain only a `RequestId`).
6+
7+
This resource is `RESOURCE_KIND_ATTACHMENT`: it primarily manages bind/unbind operations and only needs Create/Read/Delete (CRD). Because the cloud API offers no dedicated update endpoint for an existing binding, the resource is treated as immutable — any schema change triggers recreation (`ForceNew`).
8+
9+
## Goals / Non-Goals
10+
11+
**Goals:**
12+
- Provide a Terraform resource `tencentcloud_dlc_attach_user_policyr_attachment` that binds a set of DLC authorization policies to a user.
13+
- Support Create (bind via `AttachUserPolicy`), Read (query via `DescribeUserInfo`), and Delete (unbind via `DetachUserPolicy`).
14+
- Use a composite ID (`user_id` + `account_type` joined by `tccommon.FILED_SP`) since the cloud API returns no single identifier.
15+
- Follow the established provider code style (referencing `tencentcloud_igtm_strategy` for non-DATASOURCE resources), including retry logic with `tccommon.ReadRetryTimeout`/`tccommon.WriteRetryTimeout` and `tccommon.RetryError`.
16+
- Add unit tests using gomonkey mocks (no Terraform acceptance test suite) per project conventions for new resources.
17+
18+
**Non-Goals:**
19+
- No update-in-place support: the resource is immutable; changing any top-level argument recreates the resource.
20+
- No import support (ATTACHMENT resources do not include an Import section per documentation rules — only GENERAL/ATTACHMENT/CONFIG kinds include import, but this attachment has no uniquely re-queryable single id and the binding is reconstructed from request params; import is omitted to avoid ambiguity).
21+
- No data source for listing user-policy bindings.
22+
23+
## Decisions
24+
25+
### Decision 1: Resource kind and lifecycle (CRD only, immutable)
26+
The cloud API provides `AttachUserPolicy` (bind), `DescribeUserInfo` (read), and `DetachUserPolicy` (unbind), with no dedicated modify endpoint. Therefore the Terraform resource implements only Create/Read/Delete. Per the project rule for CRD-only resources, the `Id()` field is set to `ForceNew` and all other top-level fields are added to an `immutableArgs` array in the update method; if any of them changed, the update returns an error (which triggers Terraform to recreate).
27+
28+
**Why:** This matches the API surface and the project's CRD-only resource convention. **Alternative considered:** Implementing a custom update that diffs the policy_set and re-binds — rejected because the cloud API has no partial update endpoint and the attachment semantics are bind/unbind; recreation is simpler and correct.
29+
30+
### Decision 2: Composite ID composition
31+
The resource ID is composed of `user_id` and `account_type` joined by `tccommon.FILED_SP` (`user_id#account_type`). In Read/Update/Delete, the ID is split back into its parts and each part is used as a request parameter.
32+
33+
**Why:** `AttachUserPolicy` returns only `PolicySet` + `RequestId` (no id). The pair `(user_id, account_type)` uniquely identifies the user whose policies are being managed. **Alternative considered:** Using `user_id` alone — rejected because `account_type` distinguishes `TencentAccount` vs `EntraAccount` users and is required by all three APIs.
34+
35+
### Decision 3: Read strategy using DescribeUserInfo
36+
`DescribeUserInfo` returns a `UserDetailInfo` containing multiple `Policys` collections (`DataPolicyInfo`, `EnginePolicyInfo`, `CatalogPolicyInfo`, `ModelPolicyInfo`, `RowFilterInfo`). To verify the binding still exists, Read calls `DescribeUserInfo` with `Type=DataAuth` and checks whether the policies in `policy_set` are present among the user's bound data policies (matched by the `PolicyId` field returned by the API, or by the policy content fields when `PolicyId` is unavailable).
37+
38+
**Why:** There is no single "describe binding" API; `DescribeUserInfo` is the only read path that returns the user's bound policies. The `policy_id` request parameter of `DescribeUserInfo` (described as "TF 资源 ID") can be used to narrow the query when a specific policy id is known.
39+
40+
### Decision 4: PolicySet schema structure
41+
`policy_set` is a `TypeList` of `Policy` objects. The `Policy` struct has many fields; the schema exposes the fields that are meaningful as inputs for binding: `database`, `catalog`, `table`, `operation`, `policy_type`, `function`, `view`, `column`, `data_engine`, `re_auth`, `engine_generation`, `model`, and `policy_id`. Fields marked "入参不填" in the API docs (e.g. `source`, `mode`, `operator`, `create_time`, `source_id`, `source_name`, `id`, `is_admin_policy`) are output-only and not set in Create, but may be read back where applicable.
42+
43+
**Why:** Only input-eligible fields should be `Required`/`Optional`; read-only fields are `Computed`.
44+
45+
### Decision 5: Delete via DetachUserPolicy
46+
Delete calls `DetachUserPolicy` with the `user_id`, `account_type`, and the `policy_set` (and/or `policy_ids`) used at creation. Since `DetachUserPolicy` accepts both `PolicySet` and `PolicyIds`, the implementation passes the stored `policy_set` to unbind exactly the policies that were bound by this resource.
47+
48+
### Decision 6: Retry and error handling
49+
- Create/Update/Delete wrap the cloud API call in `resource.Retry(tccommon.WriteRetryTimeout, ...)` and translate errors via `tccommon.RetryError`.
50+
- Read wraps the `DescribeUserInfo` call in `resource.Retry(tccommon.ReadRetryTimeout, ...)`.
51+
- Setting the ID and other state mutations happen outside the retry block, after the retry error handling.
52+
- In Create, after the API returns, the response is checked for emptiness; `AttachUserPolicy` returns no id, so the composite ID is set from request params. The `policy_set` returned in the response is set into state.
53+
- In Read, if the response is empty or the bound policies are not found, `log.Printf("[CRUD] ...")` is emitted first to preserve the id, then `d.SetId("")`.
54+
55+
## Risks / Trade-offs
56+
57+
- **[No single resource id from API]** → Mitigated by deriving a composite ID from `user_id` + `account_type`. The user must not delete the same binding outside Terraform, otherwise Read will detect the binding is gone and remove it from state.
58+
- **[DescribeUserInfo returns multiple policy categories]** → Read focuses on `DataAuth` type and matches bound policies; if a policy is bound but not reflected in `DataPolicyInfo`, Read may consider the binding absent. Mitigated by querying with the appropriate `Type` and, when available, the `PolicyId` filter to precisely locate the bound policy.
59+
- **[Immutable resource recreation on change]** → Any change recreates the binding (unbind + bind). This is acceptable for attachment resources and matches the CRD-only convention; documented in the resource docs.
60+
- **[PolicySet matching during Read]** → The API may return policies with `PolicyId` populated. Read matches by `PolicyId` when present, otherwise by content fields. Edge case: two identical policies could cause ambiguity, which is inherent to the API design.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
## Why
2+
3+
The DLC (Data Lake Compute) product currently lacks a Terraform resource to manage the binding of authorization policies to users. Users need to bind (attach) and unbind (detach) permission policies to/from DLC sub-users through Terraform, enabling infrastructure-as-code management of DLC data access permissions. Today this binding relationship can only be managed via the console or API calls, making it difficult to keep permission grants consistent with other infrastructure definitions.
4+
5+
## What Changes
6+
7+
- Add a new Terraform resource `tencentcloud_dlc_attach_user_policyr_attachment` of kind `RESOURCE_KIND_ATTACHMENT` to manage the binding between a DLC user and authorization policies.
8+
- The resource supports Create (bind via `AttachUserPolicy`), Read (query via `DescribeUserInfo`), and Delete (unbind via `DetachUserPolicy`). There is no dedicated Update API; the resource is immutable and uses `ForceNew` for changing any parameter.
9+
- Register the new resource in `tencentcloud/provider.go` and `tencentcloud/provider.md`.
10+
- Add the resource documentation file `resource_tc_dlc_attach_user_policyr_attachment.md`.
11+
- Add unit tests using gomonkey mocks (no Terraform test suite) in `resource_tc_dlc_attach_user_policyr_attachment_test.go`.
12+
13+
## Capabilities
14+
15+
### New Capabilities
16+
- `dlc-attach-user-policy-attachment`: Manage the binding relationship between a DLC user and authorization policies, including binding (create), reading the bound policies, and unbinding (delete).
17+
18+
### Modified Capabilities
19+
<!-- No existing capabilities are modified. -->
20+
21+
## Impact
22+
23+
- **New files**:
24+
- `tencentcloud/services/dlc/resource_tc_dlc_attach_user_policyr_attachment.go` (resource CRUD implementation)
25+
- `tencentcloud/services/dlc/resource_tc_dlc_attach_user_policyr_attachment_test.go` (unit tests with gomonkey mocks)
26+
- `tencentcloud/services/dlc/resource_tc_dlc_attach_user_policyr_attachment.md` (documentation)
27+
- **Modified files**:
28+
- `tencentcloud/provider.go` (register the new resource)
29+
- `tencentcloud/provider.md` (documentation index entry)
30+
- **Cloud APIs used** (from `github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dlc/v20210125`):
31+
- `AttachUserPolicy` — bind policies to a user (Create)
32+
- `DescribeUserInfo` — read user detail info including bound policies (Read)
33+
- `DetachUserPolicy` — unbind policies from a user (Delete)
34+
- **Resource ID**: composite id composed of `user_id` + `account_type` (joined by `tccommon.FILED_SP`), used to uniquely identify the binding relationship. Since the attach API does not return a single identifier, the composite key is derived from request parameters.
35+
- **No breaking changes**: this is a purely additive change.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
## ADDED Requirements
2+
3+
### Requirement: Resource shall manage DLC user-policy binding lifecycle
4+
The Terraform provider SHALL provide a resource `tencentcloud_dlc_attach_user_policyr_attachment` of kind `RESOURCE_KIND_ATTACHMENT` that manages the binding between a DLC user and a set of authorization policies. The resource SHALL support Create (bind), Read (query), and Delete (unbind). The resource SHALL NOT support in-place Update; any change to a top-level argument SHALL trigger recreation.
5+
6+
#### Scenario: Bind policies to a user on create
7+
- **WHEN** a user applies a configuration defining `tencentcloud_dlc_attach_user_policyr_attachment` with `user_id`, `policy_set`, and `account_type`
8+
- **THEN** the provider SHALL call the DLC `AttachUserPolicy` API with the given `UserId`, `PolicySet`, and `AccountType`
9+
- **AND** the provider SHALL set the resource ID to the composite of `user_id` and `account_type` joined by `tccommon.FILED_SP`
10+
- **AND** the provider SHALL set `policy_set` in state from the API response
11+
12+
#### Scenario: Read reflects currently bound policies
13+
- **WHEN** the provider reads an existing `tencentcloud_dlc_attach_user_policyr_attachment` resource
14+
- **THEN** the provider SHALL call the DLC `DescribeUserInfo` API with the `UserId` and `AccountType` derived from the composite ID
15+
- **AND** the provider SHALL verify the bound policies are still present
16+
- **AND** if the binding is no longer present, the provider SHALL emit a `[CRUD]` log preserving the id and then set `d.SetId("")`
17+
18+
#### Scenario: Unbind policies on delete
19+
- **WHEN** a user destroys a `tencentcloud_dlc_attach_user_policyr_attachment` resource
20+
- **THEN** the provider SHALL call the DLC `DetachUserPolicy` API with the `UserId`, `AccountType`, and `PolicySet` derived from state
21+
- **AND** the resource SHALL be removed from state
22+
23+
#### Scenario: Any change recreates the resource
24+
- **WHEN** a user changes any top-level argument of an existing `tencentcloud_dlc_attach_user_policyr_attachment` resource
25+
- **THEN** the provider SHALL reject the in-place update (the update method returns an error for immutable args) and Terraform SHALL recreate the resource
26+
27+
### Requirement: Resource SHALL use a composite identifier
28+
Because the DLC `AttachUserPolicy` API returns no single resource identifier, the resource SHALL use a composite ID composed of `user_id` and `account_type` joined by `tccommon.FILED_SP`. The Read, Update, and Delete operations SHALL split the composite ID to recover `user_id` and `account_type` for use as API request parameters.
29+
30+
#### Scenario: Composite ID is split for API calls
31+
- **WHEN** the provider performs Read, Update, or Delete on `tencentcloud_dlc_attach_user_policyr_attachment`
32+
- **THEN** the provider SHALL split `d.Id()` by `tccommon.FILED_SP` into `user_id` and `account_type`
33+
- **AND** the provider SHALL use these values as the `UserId` and `AccountType` request parameters
34+
35+
### Requirement: Resource schema SHALL expose binding parameters
36+
The resource schema SHALL expose `user_id` (Required, ForceNew), `policy_set` (Required), and `account_type` (Optional, ForceNew). The `policy_set` SHALL be a list of policy objects whose input-eligible fields (`database`, `catalog`, `table`, `operation`, `policy_type`, `function`, `view`, `column`, `data_engine`, `re_auth`, `engine_generation`, `model`, `policy_id`) can be set by the user.
37+
38+
#### Scenario: Required arguments are enforced
39+
- **WHEN** a user omits `user_id` or `policy_set` in the configuration
40+
- **THEN** Terraform SHALL report a validation error before any API call is made
41+
42+
#### Scenario: account_type is optional
43+
- **WHEN** a user does not specify `account_type`
44+
- **THEN** the provider SHALL not set `AccountType` in the API request, allowing the cloud API to apply its default
45+
46+
### Requirement: Cloud API calls SHALL use retry and proper error handling
47+
Create, Read, and Delete SHALL wrap their cloud API calls in `resource.Retry` using `tccommon.WriteRetryTimeout` (for Create/Delete) and `tccommon.ReadRetryTimeout` (for Read). Errors from the cloud API SHALL be wrapped with `tccommon.RetryError`. State mutations (setting the ID and fields) SHALL occur outside the retry block, after successful retry completion.
48+
49+
#### Scenario: Transient API failure is retried
50+
- **WHEN** a DLC API call fails with a retryable error during Create, Read, or Delete
51+
- **THEN** the provider SHALL retry the call within the configured timeout and wrap the error with `tccommon.RetryError`
52+
53+
#### Scenario: Create validates non-empty response
54+
- **WHEN** the `AttachUserPolicy` API returns a nil response
55+
- **THEN** the provider SHALL return a `NonRetryableError` rather than writing an empty id to state
56+
57+
### Requirement: Resource SHALL be registered in the provider
58+
The provider SHALL register `tencentcloud_dlc_attach_user_policyr_attachment` in `tencentcloud/provider.go` and document it in `tencentcloud/provider.md`. A documentation file `resource_tc_dlc_attach_user_policyr_attachment.md` SHALL exist under `tencentcloud/services/dlc/`.
59+
60+
#### Scenario: Resource is usable in a Terraform configuration
61+
- **WHEN** a user references `tencentcloud_dlc_attach_user_policyr_attachment` in a `.tf` file
62+
- **THEN** the provider SHALL recognize the resource type and execute its CRUD handlers
63+
64+
### Requirement: Unit tests SHALL use gomonkey mocks
65+
The resource test file `resource_tc_dlc_attach_user_policyr_attachment_test.go` SHALL use gomonkey to mock the DLC cloud API client methods and test the business logic of Create/Read/Delete without relying on the Terraform acceptance test suite. The tests SHALL be runnable with `go test -gcflags=all=-l`.
66+
67+
#### Scenario: Create logic is tested with mocked API
68+
- **WHEN** the unit test invokes the Create handler with a mocked `AttachUserPolicy` that returns a valid response
69+
- **THEN** the test SHALL assert the composite ID is set and `policy_set` state is populated
70+
71+
#### Scenario: Delete logic is tested with mocked API
72+
- **WHEN** the unit test invokes the Delete handler with a mocked `DetachUserPolicy`
73+
- **THEN** the test SHALL assert `DetachUserPolicy` is called with the expected parameters

0 commit comments

Comments
 (0)