Skip to content

Commit 9d0ebc4

Browse files
authored
feat(ga2): [132980201] add new resource (#4142)
* add * add
1 parent faa500c commit 9d0ebc4

19 files changed

Lines changed: 5919 additions & 4 deletions

File tree

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ require (
4646
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.3.52
4747
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cloudaudit v1.0.1033
4848
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cls v1.3.58
49-
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.95
49+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.102
5050
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/config v1.3.80
5151
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.3.69
5252
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cwp v1.3.30
@@ -120,6 +120,7 @@ require (
120120
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/controlcenter v1.1.51
121121
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/csip v1.0.860
122122
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dasb v1.0.970
123+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ga2 v1.3.102
123124
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/gwlb v1.0.1127
124125
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/igtm v1.3.29
125126
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/mqtt v1.3.7

go.sum

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -957,8 +957,9 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.86/go.mod h
957957
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.89/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
958958
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.90/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
959959
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.94/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
960-
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.95 h1:t9H/qXsRSq9WV6ifCWXD2DGXuWSXjq7cKsSFnLbTIN4=
961960
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.95/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
961+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.102 h1:3n7BpLOPnE9Rv3Y9Jxgl/XaQX3GzktO+dEaAbtVbjSk=
962+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.102/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
962963
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/config v1.3.80 h1:chnsNBeJn3MieFLki4hpbzoml5NiTvLVzOTqYRVxQho=
963964
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/config v1.3.80/go.mod h1:B/ezGtlvDhZlleNM+2QdvZccrUi+J+fN6vMnDDsZnuM=
964965
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/controlcenter v1.1.51 h1:pGwrfCBBCt1u+EDHwfNj9NLQpvk5MVKVMcsE7SvwqM4=
@@ -995,6 +996,8 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/emr v1.3.82 h1:Hxyp0aSH
995996
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/emr v1.3.82/go.mod h1:y3+s1ScI5wA8IEGlWuoQnZJTgo9zMPkllpuKyrl912w=
996997
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/es v1.1.13 h1:1YXQtuxHwFLOLiKxxtVLjZ73XyWBtZHTnRX88jID9qo=
997998
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/es v1.1.13/go.mod h1:pIpYMasWxEcqh/YW9hr6kP78gwp8CP5Y2t3+SlyXp+s=
999+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ga2 v1.3.102 h1:tA310fsZ5o9EAzQqswoJp1Kf7p5aDDhvE3xJJ8myCvM=
1000+
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ga2 v1.3.102/go.mod h1:vrDJ9AQOh2OQbGgJCGOuGJvobr0DqBSljzwvJrM7vDk=
9981001
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/gaap v1.0.970 h1:Z82LbcEnE2m4CGvcOY024N7REhAN++xdEyE88HG2YBA=
9991002
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/gaap v1.0.970/go.mod h1:qnOT3rycGxlNy4/gmprV58Uqd9CKLUr2CL235R1AvuQ=
10001003
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/gs v1.3.58 h1:yOCIldK7GMcJUNW1YK067fiwec4Hw5n2enSjZQa0Chs=
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-05-25
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
## Context
2+
3+
GA2 endpoint groups are a child object of a listener under a global accelerator. The SDK at `tencentcloud/ga2/v20250115` provides:
4+
5+
- `CreateEndpointGroup(GlobalAcceleratorId, ListenerId, EndpointGroupType, EndpointGroupConfiguration)``{TaskId, EndpointGroupId}`
6+
- `DescribeEndpointGroups(GlobalAcceleratorId, Offset, Limit, Filters)``{EndpointGroupConfigurationSet[], TotalCount}`
7+
- `ModifyEndpointGroup(GlobalAcceleratorId, ListenerId, EndpointGroupId, ...flat fields)``{TaskId}`
8+
- `DeleteEndpointGroups(GlobalAcceleratorId, ListenerId, EndpointGroupIds)``{TaskId}`
9+
- `DescribeTaskResult(TaskId)``{Status}` (terminal `SUCCESS`)
10+
11+
There is no existing `ga2` service package, no `UseGa2V20250115Client` connectivity entry, and no Terraform resource exposing any GA2 functionality. This change introduces all three.
12+
13+
The styling baseline is `tencentcloud_igtm_monitor` (see `tencentcloud/services/igtm/resource_tc_igtm_monitor.go`): `defer tccommon.LogElapsed`, `defer tccommon.InconsistentCheck`, `tccommon.NewResourceLifeCycleHandleFuncContext`, all SDK calls wrapped in `resource.Retry(tccommon.WriteRetryTimeout, …)`, nil-safe response unpacking via `if result == nil || result.Response == nil { resource.NonRetryableError(…) }`.
14+
15+
## Goals / Non-Goals
16+
17+
**Goals:**
18+
- Resource `tencentcloud_ga2_endpoint_group` with CRUD lifecycle, importable.
19+
- Schema mirrors `CreateEndpointGroup` request 1:1 (top-level + nested block).
20+
- Composite ID `<global_accelerator_id>#<listener_id>#<endpoint_group_id>`, parsed in Read/Update/Delete and re-joined for `d.SetId`.
21+
- Wrap every SDK call in `resource.Retry`. After Create/Update/Delete, additionally poll `DescribeTaskResult` until `Status == SUCCESS`.
22+
- Service helper `DescribeGa2EndpointGroupById` performs paginated lookup using `endpoint-group-id` filter, with `Limit = 100` (API max).
23+
- Defensive nil checks on every SDK return value before pointer dereference.
24+
25+
**Non-Goals:**
26+
- Listing/data-source resources for GA2.
27+
- Other GA2 entity types (accelerator, listener, etc.) — this PR only covers endpoint groups.
28+
- Internal-only fields (`HealthCheckStatus` is read-only inside `EndpointConfigurations` and may be optionally exposed as Computed in nested elements; not modeled separately).
29+
30+
## Decisions
31+
32+
### D1: Composite ID format `<gaId>#<listenerId>#<endpointGroupId>`
33+
`CreateEndpointGroup` requires all three IDs to look up an endpoint group, and the response only returns `EndpointGroupId`. We embed the three required IDs into the resource ID using `#` as separator (consistent with project-wide convention and `tccommon.FILED_SP`). Read/Update/Delete split on `#` and validate `len(parts) == 3`.
34+
35+
### D2: Schema 1:1 with `CreateEndpointGroupRequestParams`
36+
Top-level fields:
37+
38+
| Schema field | SDK field | Type | Optional/Required |
39+
|---|---|---|---|
40+
| `global_accelerator_id` | `GlobalAcceleratorId` | TypeString | Required + ForceNew |
41+
| `listener_id` | `ListenerId` | TypeString | Required + ForceNew |
42+
| `endpoint_group_type` | `EndpointGroupType` | TypeString | Required + ForceNew (`VIRTUAL`/`DEFAULT`) |
43+
| `endpoint_group_configuration` | `EndpointGroupConfiguration` | TypeList(MaxItems=1, Elem=Resource) | Required |
44+
45+
The nested block matches every field of `EndpointGroupConfiguration`, including the inner `endpoint_configurations` list (TypeList of Resource matching `EndpointConfigurations`) and `port_overrides` list (TypeList of Resource matching `PortOverride`).
46+
47+
Computed-only attribute: `endpoint_group_id` exposes the SDK-assigned ID for convenience.
48+
49+
### D3: Update path uses flat fields, not the nested block
50+
`ModifyEndpointGroupRequestParams` is shaped differently from `CreateEndpointGroupRequestParams` — it expects most config attributes flat at top level (no `EndpointGroupConfiguration` wrapper). The Update implementation reads the nested `endpoint_group_configuration[0]` block and copies fields onto the flat `ModifyEndpointGroupRequest`. The user-facing schema remains a single nested block in both Create and Update for consistency. `endpoint_group_type` is `ForceNew` because Modify does not accept it.
51+
52+
### D4: Async polling via `DescribeTaskResult`
53+
After every `Create`/`Modify`/`Delete` SDK call, we get `TaskId`. We then call:
54+
55+
```
56+
resource.Retry(tccommon.WriteRetryTimeout*2, func() *resource.RetryError {
57+
result, err := client.DescribeTaskResultWithContext(ctx, &DescribeTaskResultRequest{TaskId: &taskId})
58+
if err != nil { return tccommon.RetryError(err) }
59+
if result == nil || result.Response == nil || result.Response.Status == nil {
60+
return resource.NonRetryableError(...)
61+
}
62+
if *result.Response.Status == "SUCCESS" { return nil }
63+
return resource.RetryableError(fmt.Errorf("task %s status=%s", taskId, *result.Response.Status))
64+
})
65+
```
66+
67+
Wrapped into `service.WaitForGa2TaskFinish(ctx, taskId)` for reuse. Terminal: `SUCCESS`. Anything else is a retryable poll until timeout.
68+
69+
### D5: Read uses pagination with API max `Limit=100`
70+
`DescribeEndpointGroups` is a list API, not a get-by-id API. Read passes `GlobalAcceleratorId` plus a `Filters: [{Name: "endpoint-group-id", Values: [<id>]}]` filter; we paginate with `Offset/Limit=100` until either we find the matching record or `len(results) < limit`. Service helper returns `(*EndpointGroupConfigurationSet, error)` where a nil pointer means not found (Read sets `d.SetId("")`).
71+
72+
### D6: Defensive nil checks throughout
73+
- All SDK responses are checked: `if result == nil || result.Response == nil { return resource.NonRetryableError(...) }` before any pointer dereference.
74+
- All response field reads in Read are guarded with `if respData.Field != nil { _ = d.Set(...) }`.
75+
- `EndpointGroupId`, `TaskId`, `Status` are explicitly checked for nil before deref.
76+
77+
### D7: Connectivity client lazy-init
78+
`UseGa2V20250115Client` follows the exact pattern of `UseIgtmV20231024Client`: cached `*ga2v20250115.Client`, lazy-initialized with `me.NewClientProfile(300)` and `WithHttpTransport(&LogRoundTripper{})`. Imports added at the top of `connectivity/client.go`.
79+
80+
## Risks / Trade-offs
81+
82+
- [Risk] Async task may exceed `WriteRetryTimeout*2` for slow regions. Mitigation: choose `WriteRetryTimeout*2` (10 min) which is the project convention for similarly async resources; if it proves insufficient, extend to `*3` per regional metrics.
83+
- [Risk] `DescribeEndpointGroups` filter `endpoint-group-id` is documented but if the API rejects unknown filter names, Read may scan all groups. Mitigation: helper falls back to filtering client-side on the response set if SDK returns an empty result with `TotalCount > 0`.
84+
- [Risk] `EndpointConfigurations.HealthCheckStatus` is a server-side computed field; we mark it Computed in the nested schema so plans don't drift.
85+
- [Trade-off] Schema duplicates flat health-check fields between `endpoint_group_configuration` block and would-be top-level, but we keep them only in the nested block for symmetry with Create. Update internally re-flattens.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
## Why
2+
3+
The Tencent Cloud Global Accelerator 2 (GA2) service exposes a full lifecycle of APIs for managing endpoint groups attached to a global accelerator listener — `CreateEndpointGroup`, `DescribeEndpointGroups`, `ModifyEndpointGroup`, and `DeleteEndpointGroups`. The provider does not yet expose any GA2 resources, so users currently cannot manage GA2 endpoint groups as infrastructure-as-code. The newly upgraded SDK (`ga2 v1.3.102`, namespace `v20250115`) now ships the required APIs, unblocking this work.
4+
5+
## What Changes
6+
7+
- Add new general (CRUD) resource `tencentcloud_ga2_endpoint_group` under a new `ga2` service package.
8+
- Resource schema fields mirror the `CreateEndpointGroup` request parameters one-to-one (top-level fields and the nested `endpoint_group_configuration` block).
9+
- Composite resource ID format: `GlobalAcceleratorId#ListenerId#EndpointGroupId` (the three IDs that uniquely identify an endpoint group instance).
10+
- Async API handling: after `CreateEndpointGroup`, `ModifyEndpointGroup`, and `DeleteEndpointGroups` return a `TaskId`, poll `DescribeTaskResult` until `Status == "SUCCESS"`.
11+
- Add service-layer helpers `DescribeGa2EndpointGroupById` and `Ga2DescribeTaskResult` in `service_tencentcloud_ga2.go`.
12+
- Register a new client entry `UseGa2V20250115Client` in `tencentcloud/connectivity/client.go`.
13+
- Register the resource in `provider.go`.
14+
- Generate `.md` documentation and `_test.go` acceptance test file using project naming conventions.
15+
16+
## Capabilities
17+
18+
### New Capabilities
19+
- `ga2-endpoint-group-resource`: Full CRUD lifecycle for a GA2 endpoint group bound to a `(global_accelerator_id, listener_id)` tuple, including async task polling on Create/Update/Delete.
20+
21+
### Modified Capabilities
22+
23+
## Impact
24+
25+
- New files:
26+
- `tencentcloud/services/ga2/resource_tc_ga2_endpoint_group.go`
27+
- `tencentcloud/services/ga2/resource_tc_ga2_endpoint_group.md`
28+
- `tencentcloud/services/ga2/resource_tc_ga2_endpoint_group_test.go`
29+
- `tencentcloud/services/ga2/service_tencentcloud_ga2.go`
30+
- Modified files:
31+
- `tencentcloud/connectivity/client.go` — add `UseGa2V20250115Client`
32+
- `tencentcloud/provider.go` — register `tencentcloud_ga2_endpoint_group`
33+
- SDK dependency: `github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ga2 v1.3.102` (namespace `v20250115`). Confirmed present in module cache; `go mod vendor` will materialize it once we import it from new code.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
## ADDED Requirements
2+
3+
### Requirement: Resource manages GA2 endpoint group lifecycle
4+
The provider SHALL expose a resource `tencentcloud_ga2_endpoint_group` that manages a Tencent Cloud Global Accelerator 2 endpoint group via `CreateEndpointGroup`, `DescribeEndpointGroups`, `ModifyEndpointGroup`, and `DeleteEndpointGroups` APIs in the `ga2/v20250115` SDK namespace.
5+
6+
#### Scenario: Create provisions and waits for task success
7+
- **WHEN** a user defines the resource with `global_accelerator_id`, `listener_id`, `endpoint_group_type`, and `endpoint_group_configuration`
8+
- **THEN** the provider SHALL call `CreateEndpointGroup`, capture the returned `EndpointGroupId` and `TaskId`, poll `DescribeTaskResult` until `Status == "SUCCESS"`, and set the resource ID to `<global_accelerator_id>#<listener_id>#<endpoint_group_id>`
9+
10+
#### Scenario: Read reflects current cloud state
11+
- **WHEN** the provider reads the resource
12+
- **THEN** it SHALL split the resource ID into three parts, call `DescribeEndpointGroups` filtered by `endpoint-group-id`, and map every non-nil response field into Terraform state
13+
14+
#### Scenario: Read clears state when not found
15+
- **WHEN** `DescribeEndpointGroups` returns no record matching the endpoint group ID
16+
- **THEN** the provider SHALL call `d.SetId("")` and return nil
17+
18+
#### Scenario: Update modifies mutable fields and waits
19+
- **WHEN** any mutable attribute under `endpoint_group_configuration` (or top-level `endpoint_configurations`/`port_overrides`/health-check fields) changes
20+
- **THEN** the provider SHALL build a `ModifyEndpointGroup` request from the new state, call the API, and poll `DescribeTaskResult` until `Status == "SUCCESS"`
21+
22+
#### Scenario: Delete removes the group and waits
23+
- **WHEN** the resource is destroyed
24+
- **THEN** the provider SHALL call `DeleteEndpointGroups` with the single-element `EndpointGroupIds` slice and poll `DescribeTaskResult` until `Status == "SUCCESS"`
25+
26+
### Requirement: Schema mirrors CreateEndpointGroup parameters
27+
The resource schema SHALL contain fields corresponding to every parameter in `CreateEndpointGroupRequestParams`.
28+
29+
#### Scenario: Top-level required fields enforced
30+
- **WHEN** a user omits `global_accelerator_id`, `listener_id`, `endpoint_group_type`, or `endpoint_group_configuration`
31+
- **THEN** Terraform SHALL return a validation error before any API call
32+
33+
#### Scenario: Nested endpoint_group_configuration mirrors SDK struct
34+
- **WHEN** the user provides an `endpoint_group_configuration` block
35+
- **THEN** every field inside (`name`, `endpoint_group_region`, `endpoint_configurations`, `check_type`, `description`, `check_port`, `context_type`, `check_send_context`, `check_recv_context`, `enable_health_check`, `connect_timeout`, `health_check_interval`, `unhealthy_threshold`, `healthy_threshold`, `forward_protocol`, `check_domain`, `check_path`, `check_method`, `status_mask`, `port_overrides`, `isp_type`, `cipher_policy_id`) SHALL map 1:1 onto the SDK request
36+
37+
#### Scenario: ForceNew on identifying and immutable fields
38+
- **WHEN** the user changes `global_accelerator_id`, `listener_id`, or `endpoint_group_type`
39+
- **THEN** Terraform SHALL plan a destroy-and-recreate, since `ModifyEndpointGroup` does not accept these fields
40+
41+
### Requirement: All SDK invocations are wrapped with retry logic
42+
Every SDK API call (Create, Read, Update, Delete, DescribeTaskResult) SHALL be invoked inside `resource.Retry(...)` using project timeout constants.
43+
44+
#### Scenario: Read uses ReadRetryTimeout
45+
- **WHEN** `DescribeEndpointGroups` is invoked from `Read` or the helper
46+
- **THEN** the call SHALL be wrapped in `resource.Retry(tccommon.ReadRetryTimeout, ...)` and use `tccommon.RetryError` for retryable errors
47+
48+
#### Scenario: Mutating calls use WriteRetryTimeout
49+
- **WHEN** `CreateEndpointGroup`, `ModifyEndpointGroup`, or `DeleteEndpointGroups` is invoked
50+
- **THEN** the call SHALL be wrapped in `resource.Retry(tccommon.WriteRetryTimeout, ...)`
51+
52+
#### Scenario: Task polling tolerates transient failures
53+
- **WHEN** `DescribeTaskResult` returns a transient error or non-`SUCCESS` status
54+
- **THEN** the helper SHALL return `resource.RetryableError` so the polling loop continues until `WriteRetryTimeout*2` elapses
55+
56+
### Requirement: Async task polling via DescribeTaskResult
57+
After every mutating API call, the provider SHALL block until the task associated with the returned `TaskId` reaches terminal `SUCCESS` state via `DescribeTaskResult`.
58+
59+
#### Scenario: Task succeeds
60+
- **WHEN** `DescribeTaskResult` returns `Status == "SUCCESS"`
61+
- **THEN** polling SHALL stop and the operation SHALL be considered complete
62+
63+
#### Scenario: Task remains in progress
64+
- **WHEN** `DescribeTaskResult` returns any non-`SUCCESS` status
65+
- **THEN** polling SHALL continue until the timeout elapses; on timeout the operation SHALL fail with the last observed status
66+
67+
### Requirement: Pagination uses API maximum page size
68+
List/query API calls SHALL use the documented maximum `Limit` to minimize round trips.
69+
70+
#### Scenario: DescribeEndpointGroups uses Limit=100
71+
- **WHEN** the service helper queries endpoint groups
72+
- **THEN** it SHALL pass `Limit=100` (the API maximum) and iterate with `Offset` until results are exhausted
73+
74+
### Requirement: Defensive nil-pointer handling
75+
The provider SHALL guard against nil pointer dereferences when reading SDK response fields.
76+
77+
#### Scenario: Nil response is non-retryable
78+
- **WHEN** any SDK call returns `result == nil` or `result.Response == nil`
79+
- **THEN** the call SHALL return `resource.NonRetryableError` with a clear message
80+
81+
#### Scenario: Nil field is skipped, not dereferenced
82+
- **WHEN** the read function processes a response field that is nil
83+
- **THEN** it SHALL skip the corresponding `d.Set(...)` call rather than dereferencing the pointer
84+
85+
### Requirement: Composite resource ID
86+
The resource ID SHALL be the string `<global_accelerator_id>#<listener_id>#<endpoint_group_id>`.
87+
88+
#### Scenario: ID is set on successful create
89+
- **WHEN** `CreateEndpointGroup` succeeds and the task reaches `SUCCESS`
90+
- **THEN** `d.SetId` SHALL be called with the three IDs joined by `#`
91+
92+
#### Scenario: ID is parsed on subsequent operations
93+
- **WHEN** Read, Update, or Delete is invoked
94+
- **THEN** the provider SHALL split `d.Id()` on `#`, validate exactly 3 segments, and use the segments for API parameters; an invalid ID format SHALL return an error

0 commit comments

Comments
 (0)