Skip to content

Commit 0cb7bf3

Browse files
committed
feat: tcr - support multiple security policy
1 parent c34a09f commit 0cb7bf3

File tree

3 files changed

+262
-18
lines changed

3 files changed

+262
-18
lines changed

tencentcloud/resource_tc_tcr_instance.go

Lines changed: 218 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,19 @@ package tencentcloud
2727
import (
2828
"context"
2929
"fmt"
30-
3130
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
3231
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
32+
tcr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tcr/v20190924"
3333
"github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper"
34+
"log"
3435
)
3536

3637
func resourceTencentCloudTcrInstance() *schema.Resource {
3738
return &schema.Resource{
3839
Create: resourceTencentCloudTcrInstanceCreate,
3940
Read: resourceTencentCloudTcrInstanceRead,
4041
Update: resourceTencentCloudTcrInstanceUpdate,
41-
Delete: resourceTencentCLoudTcrInstanceDelete,
42+
Delete: resourceTencentCloudTcrInstanceDelete,
4243
Importer: &schema.ResourceImporter{
4344
State: schema.ImportStatePassthrough,
4445
},
@@ -67,6 +68,35 @@ func resourceTencentCloudTcrInstance() *schema.Resource {
6768
Default: false,
6869
Description: "Control public network access.",
6970
},
71+
"security_policy": {
72+
Type: schema.TypeSet,
73+
Optional: true,
74+
Description: "Public network access allowlist policies of the TCR instance.",
75+
Elem: &schema.Resource{
76+
Schema: map[string]*schema.Schema{
77+
"cidr_block": {
78+
Type: schema.TypeString,
79+
Optional: true,
80+
Description: "The public network IP address of the access source.",
81+
},
82+
"description": {
83+
Type: schema.TypeString,
84+
Optional: true,
85+
Description: "Remarks of policy.",
86+
},
87+
"index": {
88+
Type: schema.TypeInt,
89+
Computed: true,
90+
Description: "Index of policy.",
91+
},
92+
"version": {
93+
Type: schema.TypeString,
94+
Computed: true,
95+
Description: "Version of policy.",
96+
},
97+
},
98+
},
99+
},
70100
//Computed values
71101
"status": {
72102
Type: schema.TypeString,
@@ -104,17 +134,23 @@ func resourceTencentCloudTcrInstanceCreate(d *schema.ResourceData, meta interfac
104134
logId := getLogId(contextNil)
105135
ctx := context.WithValue(context.TODO(), logIdKey, logId)
106136

107-
tcrService := TCRService{client: meta.(*TencentCloudClient).apiV3Conn}
137+
client := meta.(*TencentCloudClient).apiV3Conn
138+
tcrService := TCRService{client: client}
108139

109140
var (
110141
name = d.Get("name").(string)
111142
insType = d.Get("instance_type").(string)
112143
outErr, inErr error
113144
instanceId string
114145
instanceStatus string
115-
operation bool
146+
operation = d.Get("open_public_operation").(bool)
116147
)
117148

149+
// Check if security_policy but open_public_operation is false
150+
if _, ok := d.GetOk("security_policy"); ok && !operation {
151+
return fmt.Errorf("`open_public_operation` must be `true` if `security_policy` set")
152+
}
153+
118154
outErr = resource.Retry(writeRetryTimeout, func() *resource.RetryError {
119155
instanceId, inErr = tcrService.CreateTCRInstance(ctx, name, insType, map[string]string{})
120156
if inErr != nil {
@@ -147,9 +183,11 @@ func resourceTencentCloudTcrInstanceCreate(d *schema.ResourceData, meta interfac
147183
return err
148184
}
149185
if instanceStatus == "Running" {
186+
openPublicOperation, ok := d.GetOk("open_public_operation")
187+
operation = openPublicOperation.(bool)
188+
150189
outErr = resource.Retry(writeRetryTimeout, func() *resource.RetryError {
151-
if v, ok := d.GetOk("open_public_operation"); ok {
152-
operation = v.(bool)
190+
if ok {
153191
if operation {
154192
inErr = tcrService.ManageTCRExternalEndpoint(ctx, instanceId, "Create")
155193
} else {
@@ -164,6 +202,38 @@ func resourceTencentCloudTcrInstanceCreate(d *schema.ResourceData, meta interfac
164202
if outErr != nil {
165203
return outErr
166204
}
205+
206+
if raw, ok := d.GetOk("security_policy"); ok && operation {
207+
// Waiting for External EndPoint opened
208+
err = resource.Retry(5*readRetryTimeout, func() *resource.RetryError {
209+
var (
210+
status string
211+
)
212+
status, _, err = tcrService.DescribeExternalEndpointStatus(ctx, instanceId)
213+
if err != nil {
214+
return resource.NonRetryableError(fmt.Errorf("an error occured during DescribeExternalEndpointStatus: %s", err.Error()))
215+
}
216+
217+
if status == "Opened" {
218+
return nil
219+
}
220+
221+
if status == "Opening" {
222+
return resource.RetryableError(fmt.Errorf("external endpoint status is `%s`, retrying", status))
223+
}
224+
225+
return resource.NonRetryableError(fmt.Errorf("unexpected external endpoint status: `%s`", status))
226+
})
227+
228+
if err != nil {
229+
return err
230+
}
231+
if err := resourceTencentCloudTcrSecurityPolicyAdd(d, meta, raw.(*schema.Set).List()); err != nil {
232+
return err
233+
}
234+
} else if !operation {
235+
log.Printf("[WARN] `open_public_operation` was not opened, skip `security_policy` set.")
236+
}
167237
}
168238

169239
if tags := helper.GetTags(d, "tags"); len(tags) > 0 {
@@ -185,7 +255,8 @@ func resourceTencentCloudTcrInstanceRead(d *schema.ResourceData, meta interface{
185255
ctx := context.WithValue(context.TODO(), logIdKey, logId)
186256

187257
var outErr, inErr error
188-
tcrService := TCRService{client: meta.(*TencentCloudClient).apiV3Conn}
258+
client := meta.(*TencentCloudClient).apiV3Conn
259+
tcrService := TCRService{client: client}
189260
instance, has, outErr := tcrService.DescribeTCRInstanceById(ctx, d.Id())
190261
if outErr != nil {
191262
outErr = resource.Retry(readRetryTimeout, func() *resource.RetryError {
@@ -234,6 +305,31 @@ func resourceTencentCloudTcrInstanceRead(d *schema.ResourceData, meta interface{
234305
_ = d.Set("internal_end_point", instance.InternalEndpoint)
235306
_ = d.Set("public_status", publicStatus)
236307

308+
request := tcr.NewDescribeSecurityPoliciesRequest()
309+
request.RegistryId = helper.String(d.Id())
310+
response, err := client.UseTCRClient().DescribeSecurityPolicies(request)
311+
if err == nil {
312+
if response.Response.SecurityPolicySet != nil {
313+
securityPolicySet := response.Response.SecurityPolicySet
314+
policies := make([]interface{}, 0, len(securityPolicySet))
315+
for i := range securityPolicySet {
316+
item := securityPolicySet[i]
317+
policy := make(map[string]interface{})
318+
policy["cidr_block"] = *item.CidrBlock
319+
policy["description"] = *item.Description
320+
policy["index"] = *item.PolicyIndex
321+
policy["version"] = *item.PolicyVersion
322+
policies = append(policies, policy)
323+
}
324+
if err := d.Set("security_policy", policies); err != nil {
325+
return err
326+
}
327+
}
328+
} else {
329+
_ = d.Set("security_policy", make([]interface{}, 0))
330+
log.Printf("[WARN] %s error: %s", request.GetAction(), err.Error())
331+
}
332+
237333
tags := make(map[string]string, len(instance.TagSpecification.Tags))
238334
for _, tag := range instance.TagSpecification.Tags {
239335
tags[*tag.Key] = *tag.Value
@@ -259,16 +355,13 @@ func resourceTencentCloudTcrInstanceUpdate(d *schema.ResourceData, meta interfac
259355
if d.HasChange("open_public_operation") {
260356
operation = d.Get("open_public_operation").(bool)
261357
outErr = resource.Retry(writeRetryTimeout, func() *resource.RetryError {
262-
if v, ok := d.GetOk("open_public_operation"); ok {
263-
operation = v.(bool)
264-
if operation {
265-
inErr = tcrService.ManageTCRExternalEndpoint(ctx, instanceId, "Create")
266-
} else {
267-
inErr = tcrService.ManageTCRExternalEndpoint(ctx, instanceId, "Delete")
268-
}
269-
if inErr != nil {
270-
return retryError(inErr)
271-
}
358+
if operation {
359+
inErr = tcrService.ManageTCRExternalEndpoint(ctx, instanceId, "Create")
360+
} else {
361+
inErr = tcrService.ManageTCRExternalEndpoint(ctx, instanceId, "Delete")
362+
}
363+
if inErr != nil {
364+
return retryError(inErr)
272365
}
273366
return nil
274367
})
@@ -277,6 +370,53 @@ func resourceTencentCloudTcrInstanceUpdate(d *schema.ResourceData, meta interfac
277370
}
278371
}
279372

373+
if d.HasChange("security_policy") {
374+
var err error
375+
// Waiting for External EndPoint opened
376+
err = resource.Retry(5*readRetryTimeout, func() *resource.RetryError {
377+
var (
378+
status string
379+
)
380+
status, _, err = tcrService.DescribeExternalEndpointStatus(ctx, instanceId)
381+
if err != nil {
382+
return resource.NonRetryableError(fmt.Errorf("an error occured during DescribeExternalEndpointStatus: %s", err.Error()))
383+
}
384+
385+
if status == "Opened" {
386+
return nil
387+
}
388+
389+
if status == "Opening" {
390+
return resource.RetryableError(fmt.Errorf("external endpoint status is `%s`, retrying", status))
391+
}
392+
393+
return resource.NonRetryableError(fmt.Errorf("unexpected external endpoint status: `%s`", status))
394+
})
395+
396+
if err != nil {
397+
return err
398+
}
399+
400+
o, n := d.GetChange("security_policy")
401+
os := o.(*schema.Set)
402+
ns := n.(*schema.Set)
403+
add := ns.Difference(os).List()
404+
remove := os.Difference(ns).List()
405+
if len(remove) > 0 {
406+
err := resourceTencentCloudTcrSecurityPolicyRemove(d, meta, remove)
407+
if err != nil {
408+
return err
409+
}
410+
}
411+
if len(add) > 0 {
412+
err := resourceTencentCloudTcrSecurityPolicyAdd(d, meta, add)
413+
if err != nil {
414+
return err
415+
}
416+
}
417+
d.SetPartial("security_policy")
418+
}
419+
280420
if d.HasChange("tags") {
281421
oldTags, newTags := d.GetChange("tags")
282422
replaceTags, deleteTags := diffTags(oldTags.(map[string]interface{}), newTags.(map[string]interface{}))
@@ -292,7 +432,7 @@ func resourceTencentCloudTcrInstanceUpdate(d *schema.ResourceData, meta interfac
292432
return resourceTencentCloudTcrInstanceRead(d, meta)
293433
}
294434

295-
func resourceTencentCLoudTcrInstanceDelete(d *schema.ResourceData, meta interface{}) error {
435+
func resourceTencentCloudTcrInstanceDelete(d *schema.ResourceData, meta interface{}) error {
296436
defer logElapsed("resource.tencentcloud_tcr_instance.delete")()
297437

298438
logId := getLogId(contextNil)
@@ -338,3 +478,63 @@ func resourceTencentCLoudTcrInstanceDelete(d *schema.ResourceData, meta interfac
338478

339479
return nil
340480
}
481+
482+
func resourceTencentCloudTcrSecurityPolicyAdd(d *schema.ResourceData, meta interface{}, add []interface{}) error {
483+
client := meta.(*TencentCloudClient).apiV3Conn
484+
request := tcr.NewCreateMultipleSecurityPolicyRequest()
485+
request.RegistryId = helper.String(d.Id())
486+
487+
for _, i := range add {
488+
dMap := i.(map[string]interface{})
489+
policy := &tcr.SecurityPolicy{}
490+
if cidr, ok := dMap["cidr_block"]; ok {
491+
policy.CidrBlock = helper.String(cidr.(string))
492+
}
493+
if desc, ok := dMap["description"]; ok {
494+
policy.Description = helper.String(desc.(string))
495+
}
496+
if index, ok := dMap["index"]; ok {
497+
policy.PolicyIndex = helper.IntInt64(index.(int))
498+
}
499+
if version, ok := dMap["version"]; ok {
500+
policy.PolicyVersion = helper.String(version.(string))
501+
}
502+
request.SecurityGroupPolicySet = append(request.SecurityGroupPolicySet, policy)
503+
}
504+
505+
_, err := client.UseTCRClient().CreateMultipleSecurityPolicy(request)
506+
if err != nil {
507+
return err
508+
}
509+
return nil
510+
}
511+
512+
func resourceTencentCloudTcrSecurityPolicyRemove(d *schema.ResourceData, meta interface{}, remove []interface{}) error {
513+
client := meta.(*TencentCloudClient).apiV3Conn
514+
request := tcr.NewDeleteMultipleSecurityPolicyRequest()
515+
request.RegistryId = helper.String(d.Id())
516+
517+
for _, i := range remove {
518+
dMap := i.(map[string]interface{})
519+
policy := &tcr.SecurityPolicy{}
520+
if cidr, ok := dMap["cidr_block"]; ok {
521+
policy.CidrBlock = helper.String(cidr.(string))
522+
}
523+
if desc, ok := dMap["description"]; ok {
524+
policy.Description = helper.String(desc.(string))
525+
}
526+
if index, ok := dMap["index"]; ok {
527+
policy.PolicyIndex = helper.IntInt64(index.(int))
528+
}
529+
if version, ok := dMap["version"]; ok {
530+
policy.PolicyVersion = helper.String(version.(string))
531+
}
532+
request.SecurityGroupPolicySet = append(request.SecurityGroupPolicySet, policy)
533+
}
534+
535+
_, err := client.UseTCRClient().DeleteMultipleSecurityPolicy(request)
536+
if err != nil {
537+
return err
538+
}
539+
return nil
540+
}

tencentcloud/resource_tc_tcr_instance_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,19 @@ func TestAccTencentCloudTCRInstance_basic_and_update(t *testing.T) {
4141
resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "tags.test", "test"),
4242
resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "delete_bucket", "true"),
4343
resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "open_public_operation", "true"),
44+
resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "security_policy.#", "2"),
45+
resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "security_policy.0.cidr_block", "192.168.1.1/24"),
46+
resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "security_policy.1.cidr_block", "10.0.0.1/16"),
47+
),
48+
Destroy: false,
49+
},
50+
{
51+
Config: testAccTCRInstance_basic_update_security,
52+
Check: resource.ComposeAggregateTestCheckFunc(
53+
testAccCheckTCRInstanceExists("tencentcloud_tcr_instance.mytcr_instance"),
54+
resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "open_public_operation", "true"),
55+
resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "security_policy.#", "1"),
56+
resource.TestCheckResourceAttr("tencentcloud_tcr_instance.mytcr_instance", "security_policy.0.cidr_block", "192.168.1.1/24"),
4457
),
4558
},
4659
},
@@ -109,7 +122,32 @@ resource "tencentcloud_tcr_instance" "mytcr_instance" {
109122
instance_type = "basic"
110123
delete_bucket = true
111124
open_public_operation = true
125+
security_policy {
126+
cidr_block = "192.168.1.1/24"
127+
}
128+
security_policy {
129+
cidr_block = "10.0.0.1/16"
130+
}
131+
112132
tags ={
113133
test = "test"
114134
}
115135
}`
136+
137+
138+
const testAccTCRInstance_basic_update_security = `
139+
resource "tencentcloud_tcr_instance" "mytcr_instance" {
140+
name = "testacctcrinstance1"
141+
instance_type = "basic"
142+
delete_bucket = true
143+
open_public_operation = true
144+
145+
security_policy {
146+
cidr_block = "192.168.1.1/24"
147+
}
148+
149+
tags ={
150+
test = "test"
151+
}
152+
}
153+
`

website/docs/r/tcr_instance.html.markdown

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,14 @@ The following arguments are supported:
3232
* `name` - (Required, ForceNew) Name of the TCR instance.
3333
* `delete_bucket` - (Optional) Indicate to delete the COS bucket which is auto-created with the instance or not.
3434
* `open_public_operation` - (Optional) Control public network access.
35+
* `security_policy` - (Optional) Public network access allowlist policies of the TCR instance.
3536
* `tags` - (Optional) The available tags within this TCR instance.
3637

38+
The `security_policy` object supports the following:
39+
40+
* `cidr_block` - (Optional) The public network IP address of the access source.
41+
* `description` - (Optional) Remarks of policy.
42+
3743
## Attributes Reference
3844

3945
In addition to all arguments above, the following attributes are exported:

0 commit comments

Comments
 (0)