From 6d1c24bccba5a1b77a27b798a4c838d90ed32114 Mon Sep 17 00:00:00 2001 From: notone <42347556+notone0010@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:48:45 +0800 Subject: [PATCH 1/5] Update CHANGELOG.md --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0834728e..a1e5aee5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.17.2 (Nov 22, 2024) + +BUGFIX: + +- `ksyun_tag_v2_attachment` 修复批量创建时出现的资源找不到的问题 + ## 1.17.1 (Oct 21, 2024) IMPROVEMENTS: @@ -818,4 +824,4 @@ RESOURCES: * iam group delete * iam policy create * iam policy read -* iam policy delete \ No newline at end of file +* iam policy delete From c89db15a8e27fd626f5f65b1a912336ca362adf5 Mon Sep 17 00:00:00 2001 From: "Sk.Lv" Date: Tue, 26 Nov 2024 14:47:32 +0800 Subject: [PATCH 2/5] feat: alb, bws, ebs, nat embeded tags --- ksyun/resource_ksyun_alb.go | 2 ++ ksyun/resource_ksyun_bws.go | 2 ++ ksyun/resource_ksyun_nat.go | 2 ++ ksyun/resource_ksyun_volume.go | 13 +++++---- ksyun/service_ksyun_alb.go | 38 ++++++++++++++++++++++---- ksyun/service_ksyun_bws.go | 42 ++++++++++++++++++++++++++--- ksyun/service_ksyun_tag.go | 2 +- ksyun/service_ksyun_volume.go | 33 ++++++++++++++++++++--- ksyun/service_ksyun_vpc.go | 33 +++++++++++++++++++++-- website/docs/r/alb.html.markdown | 1 + website/docs/r/bws.html.markdown | 1 + website/docs/r/nat.html.markdown | 1 + website/docs/r/volume.html.markdown | 1 + 13 files changed, 152 insertions(+), 19 deletions(-) diff --git a/ksyun/resource_ksyun_alb.go b/ksyun/resource_ksyun_alb.go index 9b9338ae..84c6922e 100644 --- a/ksyun/resource_ksyun_alb.go +++ b/ksyun/resource_ksyun_alb.go @@ -211,6 +211,8 @@ func resourceKsyunAlb() *schema.Resource { }, }, + "tags": tagsSchema(), + // computed values "create_time": { Type: schema.TypeString, diff --git a/ksyun/resource_ksyun_bws.go b/ksyun/resource_ksyun_bws.go index 2f71645a..dc8521ef 100644 --- a/ksyun/resource_ksyun_bws.go +++ b/ksyun/resource_ksyun_bws.go @@ -25,6 +25,7 @@ package ksyun import ( "fmt" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) @@ -76,6 +77,7 @@ func resourceKsyunBandWidthShare() *schema.Resource { Default: 0, Description: "ID of the project.", }, + "tags": tagsSchema(), }, } } diff --git a/ksyun/resource_ksyun_nat.go b/ksyun/resource_ksyun_nat.go index dbe52271..0e44a5b4 100644 --- a/ksyun/resource_ksyun_nat.go +++ b/ksyun/resource_ksyun_nat.go @@ -137,6 +137,8 @@ func resourceKsyunNat() *schema.Resource { Description: "The PurchaseTime of the Nat, value range [1, 36]. If charge_type is Monthly this Field is Required.", }, + "tags": tagsSchema(), + "nat_ip_set": { Type: schema.TypeList, Computed: true, diff --git a/ksyun/resource_ksyun_volume.go b/ksyun/resource_ksyun_volume.go index 24b9718c..8410ba07 100644 --- a/ksyun/resource_ksyun_volume.go +++ b/ksyun/resource_ksyun_volume.go @@ -33,9 +33,10 @@ package ksyun import ( "fmt" - "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "time" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) @@ -64,10 +65,10 @@ func resourceKsyunVolume() *schema.Resource { Optional: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ - //"SSD2.0", - //"SSD3.0", - //"EHDD", - //"SATA2.0", + // "SSD2.0", + // "SSD3.0", + // "EHDD", + // "SATA2.0", "SSD3.0", "EHDD", "ESSD_PL0", @@ -151,6 +152,8 @@ func resourceKsyunVolume() *schema.Resource { DiffSuppressFunc: kecDiskSnapshotIdDiffSuppress, Description: "When the cloud disk snapshot opens, the snapshot id is entered.", }, + + "tags": tagsSchema(), }, } } diff --git a/ksyun/service_ksyun_alb.go b/ksyun/service_ksyun_alb.go index 6b729afd..2665a29c 100644 --- a/ksyun/service_ksyun_alb.go +++ b/ksyun/service_ksyun_alb.go @@ -79,6 +79,10 @@ func (alb *AlbService) readAlb(d *schema.ResourceData, albId string, allProject } } + if _, ok := d.GetOk("tags"); ok { + req["IsContainTag"] = true + } + results, err = alb.readAlbs(req) if err != nil { return data, err @@ -306,11 +310,14 @@ func (alb *AlbService) ModifyAlb(d *schema.ResourceData, r *schema.Resource) (er calls = append(calls, modifyAlbCall) } - // tagService := TagService{s.client} - // tagCall, err := tagService.ReplaceResourcesTagsWithResourceCall(d, r, "eip", true, false) - // if err != nil { - // return err - // } + if d.HasChange("tags") { + tagService := TagService{alb.client} + tagsCall, err := tagService.ReplaceResourcesTagsWithResourceCall(d, r, "loadbalancer", true, false) + if err != nil { + return err + } + calls = append(calls, tagsCall) + } return ksyunApiCallNew(calls, d, alb.client, true) } @@ -438,6 +445,18 @@ func (alb *AlbService) ReadAndSetAlb(d *schema.ResourceData, r *schema.Resource) extra["ModifyProtection"] = SdkResponseMapping{ Field: "modification_protection", } + extra["TagSet"] = SdkResponseMapping{ + Field: "tags", + FieldRespFunc: func(i interface{}) interface{} { + tags := i.([]interface{}) + tagMap := make(map[string]interface{}) + for _, tag := range tags { + _m := tag.(map[string]interface{}) + tagMap[_m["TagKey"].(string)] = _m["TagValue"].(string) + } + return tagMap + }, + } SdkResponseAutoResourceData(d, r, data, extra) return nil } @@ -535,6 +554,15 @@ func (alb *AlbService) CreateAlb(d *schema.ResourceData, r *schema.Resource) (er } } + if d.HasChange("tags") { + tagsService := TagService{client: alb.client} + tagsCall, err := tagsService.ReplaceResourcesTagsWithResourceCall(d, r, "loadbalancer", false, false) + if err != nil { + return err + } + calls = append(calls, tagsCall) + } + return ksyunApiCallNew(calls, d, alb.client, true) } diff --git a/ksyun/service_ksyun_bws.go b/ksyun/service_ksyun_bws.go index bb77c16a..500dd6e8 100644 --- a/ksyun/service_ksyun_bws.go +++ b/ksyun/service_ksyun_bws.go @@ -55,6 +55,11 @@ func (s *BwsService) ReadBandWidthShare(d *schema.ResourceData, bwsId string) (d if err != nil { return data, err } + + if _, ok := d.GetOk("tags"); ok { + req["IsContainTag"] = true + } + results, err = s.ReadBandWidthShares(req) if err != nil { return data, err @@ -81,7 +86,20 @@ func (s *BwsService) ReadAndSetBandWidthShare(d *schema.ResourceData, r *schema. return resource.NonRetryableError(fmt.Errorf("error on reading bandWidthShare %q, %s", d.Id(), callErr)) } } else { - SdkResponseAutoResourceData(d, r, data, chargeExtraForVpc(data)) + extra := chargeExtraForVpc(data) + extra["TagSet"] = SdkResponseMapping{ + Field: "tags", + FieldRespFunc: func(i interface{}) interface{} { + tags := i.([]interface{}) + tagMap := make(map[string]interface{}) + for _, tag := range tags { + _m := tag.(map[string]interface{}) + tagMap[_m["TagKey"].(string)] = _m["TagValue"].(string) + } + return tagMap + }, + } + SdkResponseAutoResourceData(d, r, data, extra) return nil } }) @@ -171,7 +189,12 @@ func (s *BwsService) CreateBandWidthShare(d *schema.ResourceData, r *schema.Reso if err != nil { return err } - return ksyunApiCallNew([]ApiCall{call}, d, s.client, true) + tagsService := TagService{client: s.client} + tagsCall, err := tagsService.ReplaceResourcesTagsWithResourceCall(d, r, "bws", false, false) + if err != nil { + return err + } + return ksyunApiCallNew([]ApiCall{call, tagsCall}, d, s.client, true) } func (s *BwsService) ModifyBandWidthShareProjectCall(d *schema.ResourceData, resource *schema.Resource) (callback ApiCall, err error) { @@ -199,6 +222,7 @@ func (s *BwsService) ModifyBandWidthShareProjectCall(d *schema.ResourceData, res func (s *BwsService) ModifyBandWidthShareCall(d *schema.ResourceData, r *schema.Resource) (callback ApiCall, err error) { transform := map[string]SdkReqTransform{ "project_id": {Ignore: true}, + "tags": {Ignore: true}, } req, err := SdkRequestAutoMapping(d, r, true, transform, nil, SdkReqParameter{ false, @@ -227,15 +251,27 @@ func (s *BwsService) ModifyBandWidthShareCall(d *schema.ResourceData, r *schema. } func (s *BwsService) ModifyBandWidthShare(d *schema.ResourceData, r *schema.Resource) (err error) { + var calls []ApiCall projectCall, err := s.ModifyBandWidthShareProjectCall(d, r) if err != nil { return err } + calls = append(calls, projectCall) call, err := s.ModifyBandWidthShareCall(d, r) if err != nil { return err } - return ksyunApiCallNew([]ApiCall{projectCall, call}, d, s.client, true) + calls = append(calls, call) + if d.HasChange("tags") { + tagsService := TagService{client: s.client} + tagsCall, err := tagsService.ReplaceResourcesTagsWithResourceCall(d, r, "bws", true, false) + if err != nil { + return err + } + calls = append(calls, tagsCall) + } + + return ksyunApiCallNew(calls, d, s.client, true) } func (s *BwsService) RemoveBandWidthShareCall(d *schema.ResourceData) (callback ApiCall, err error) { diff --git a/ksyun/service_ksyun_tag.go b/ksyun/service_ksyun_tag.go index ee6296a3..f211cb1f 100644 --- a/ksyun/service_ksyun_tag.go +++ b/ksyun/service_ksyun_tag.go @@ -580,7 +580,7 @@ func (s *TagService) ReplaceResourcesTagsWithResourceCall(d *schema.ResourceData if err != nil { return callback, err } - if len(req) > 0 { + if len(req) > 0 || d.HasChange("tags") { req["ResourceType"] = resourceType return s.ReplaceResourcesTagsCommonCall(req, disableDryRun) } diff --git a/ksyun/service_ksyun_volume.go b/ksyun/service_ksyun_volume.go index 7c582473..7c3c4e18 100644 --- a/ksyun/service_ksyun_volume.go +++ b/ksyun/service_ksyun_volume.go @@ -128,7 +128,21 @@ func (s *EbsService) ReadAndSetVolume(d *schema.ResourceData, r *schema.Resource return resource.NonRetryableError(fmt.Errorf("error on reading volume %q, %s", d.Id(), callErr)) } } else { - SdkResponseAutoResourceData(d, r, data, chargeExtraForVpc(data)) + extra := chargeExtraForVpc(data) + extra["Tags"] = SdkResponseMapping{ + Field: "tags", + FieldRespFunc: func(i interface{}) interface{} { + tags := i.([]interface{}) + tagMap := make(map[string]interface{}) + for _, tag := range tags { + _m := tag.(map[string]interface{}) + tagMap[_m["TagKey"].(string)] = _m["TagValue"].(string) + } + return tagMap + }, + } + + SdkResponseAutoResourceData(d, r, data, extra) return nil } }) @@ -161,6 +175,7 @@ func (s *EbsService) ReadAndSetVolumes(d *schema.ResourceData, r *schema.Resourc func (s *EbsService) CreateVolumeCall(d *schema.ResourceData, r *schema.Resource) (callback ApiCall, err error) { transform := map[string]SdkReqTransform{ "online_resize": {Ignore: true}, + "tags": {Ignore: true}, } req, err := SdkRequestAutoMapping(d, r, false, transform, nil, SdkReqParameter{ onlyTransform: false, @@ -199,7 +214,12 @@ func (s *EbsService) CreateVolume(d *schema.ResourceData, r *schema.Resource) (e if err != nil { return err } - return ksyunApiCallNew([]ApiCall{call}, d, s.client, true) + tagsService := TagService{client: s.client} + tagsCall, err := tagsService.ReplaceResourcesTagsWithResourceCall(d, r, "volume", false, false) + if err != nil { + return err + } + return ksyunApiCallNew([]ApiCall{call, tagsCall}, d, s.client, true) } func (s *EbsService) ModifyVolumeProjectCall(d *schema.ResourceData, resource *schema.Resource) (callback ApiCall, err error) { @@ -229,6 +249,7 @@ func (s *EbsService) ModifyVolumeInfoCall(d *schema.ResourceData, r *schema.Reso "project_id": {Ignore: true}, "size": {Ignore: true}, "online_resize": {Ignore: true}, + "tags": {Ignore: true}, } req, err := SdkRequestAutoMapping(d, r, true, transform, nil, SdkReqParameter{ false, @@ -323,7 +344,13 @@ func (s *EbsService) ModifyVolume(d *schema.ResourceData, r *schema.Resource) (e if err != nil { return err } - return ksyunApiCallNew([]ApiCall{projectCall, infoCall, call}, d, s.client, true) + + tagsService := TagService{client: s.client} + tagsCall, err := tagsService.ReplaceResourcesTagsWithResourceCall(d, r, "volume", true, false) + if err != nil { + return err + } + return ksyunApiCallNew([]ApiCall{projectCall, infoCall, call, tagsCall}, d, s.client, true) } func (s *EbsService) RemoveVolumeCall(d *schema.ResourceData) (callback ApiCall, err error) { diff --git a/ksyun/service_ksyun_vpc.go b/ksyun/service_ksyun_vpc.go index f6702b76..ba851764 100644 --- a/ksyun/service_ksyun_vpc.go +++ b/ksyun/service_ksyun_vpc.go @@ -1017,6 +1017,9 @@ func (s *VpcService) ReadNat(d *schema.ResourceData, natId string) (data map[str if err != nil { return data, err } + if _, ok := d.GetOk("tags"); ok { + req["IsContainTag"] = true + } results, err = s.ReadNats(req) if err != nil { return data, err @@ -1081,7 +1084,21 @@ func (s *VpcService) ReadAndSetNat(d *schema.ResourceData, r *schema.Resource) ( return resource.NonRetryableError(fmt.Errorf("error on reading nat %q, %s", d.Id(), callErr)) } } else { - SdkResponseAutoResourceData(d, r, data, chargeExtraForVpc(data)) + extra := chargeExtraForVpc(data) + extra["TagSet"] = SdkResponseMapping{ + Field: "tags", + FieldRespFunc: func(i interface{}) interface{} { + tags := i.([]interface{}) + tagMap := make(map[string]interface{}) + for _, tag := range tags { + _m := tag.(map[string]interface{}) + tagMap[_m["TagKey"].(string)] = _m["TagValue"].(string) + } + return tagMap + }, + } + + SdkResponseAutoResourceData(d, r, data, extra) return nil } }) @@ -1119,7 +1136,12 @@ func (s *VpcService) CreateNat(d *schema.ResourceData, r *schema.Resource) (err if err != nil { return err } - return ksyunApiCallNew([]ApiCall{call}, d, s.client, true) + tagsService := TagService{client: s.client} + tagsCall, err := tagsService.ReplaceResourcesTagsWithResourceCall(d, r, "nat", false, false) + if err != nil { + return err + } + return ksyunApiCallNew([]ApiCall{call, tagsCall}, d, s.client, true) } func (s *VpcService) ModifyNatCall(d *schema.ResourceData, r *schema.Resource) (callback ApiCall, err error) { @@ -1267,6 +1289,13 @@ func (s *VpcService) ModifyNat(d *schema.ResourceData, r *schema.Resource) (err return err } apiProcess.PutCalls(natIPCall) + + tagsService := TagService{client: s.client} + tagsCall, err := tagsService.ReplaceResourcesTagsWithResourceCall(d, r, "nat", true, false) + if err != nil { + return err + } + apiProcess.PutCalls(tagsCall) return apiProcess.Run() } diff --git a/website/docs/r/alb.html.markdown b/website/docs/r/alb.html.markdown index df50457c..c718bab9 100644 --- a/website/docs/r/alb.html.markdown +++ b/website/docs/r/alb.html.markdown @@ -71,6 +71,7 @@ The following arguments are supported: * `project_id` - (Optional) The ID of the project. * `state` - (Optional) The state of the ALB, Valid Values:'start', 'stop'. * `subnet_id` - (Optional, ForceNew) The Id of Subnet that's type is **Reserve**. It not be empty, when 'alb_type' as '**internal**'. +* `tags` - (Optional) the tags of the resource. The `klog_info` object supports the following: diff --git a/website/docs/r/bws.html.markdown b/website/docs/r/bws.html.markdown index 7b983e5b..814ea300 100644 --- a/website/docs/r/bws.html.markdown +++ b/website/docs/r/bws.html.markdown @@ -30,6 +30,7 @@ The following arguments are supported: * `line_id` - (Required, ForceNew) The id of the line. * `band_width_share_name` - (Optional) name of the BWS. * `project_id` - (Optional) ID of the project. +* `tags` - (Optional) the tags of the resource. ## Attributes Reference diff --git a/website/docs/r/nat.html.markdown b/website/docs/r/nat.html.markdown index 27bb62c4..d0992386 100644 --- a/website/docs/r/nat.html.markdown +++ b/website/docs/r/nat.html.markdown @@ -45,6 +45,7 @@ The following arguments are supported: * `nat_type` - (Optional, ForceNew) Type of the NAT, valid values: 'public'. * `project_id` - (Optional) ID of the project. * `purchase_time` - (Optional, ForceNew) The PurchaseTime of the Nat, value range [1, 36]. If charge_type is Monthly this Field is Required. +* `tags` - (Optional) the tags of the resource. ## Attributes Reference diff --git a/website/docs/r/volume.html.markdown b/website/docs/r/volume.html.markdown index 3e45d91e..43bc3491 100644 --- a/website/docs/r/volume.html.markdown +++ b/website/docs/r/volume.html.markdown @@ -41,6 +41,7 @@ The following arguments are supported: * `project_id` - (Optional) The ID of the project. * `size` - (Optional) The capacity of the EBS volume, in GB. Value range: [10, 32000], Default is 10. * `snapshot_id` - (Optional, ForceNew) When the cloud disk snapshot opens, the snapshot id is entered. +* `tags` - (Optional) the tags of the resource. * `volume_desc` - (Optional) The description of the EBS volume. * `volume_name` - (Optional) The name of the EBS volume. * `volume_type` - (Optional, ForceNew) The type of the EBS volume. Valid values:ESSD_PL0/ESSD_PL1/ESSD_PL2/ESSD_PL3/SSD3.0/EHDD, default is `SSD3.0`. From e3b7c0135de5e1ead454e78a501aabe300c3828c Mon Sep 17 00:00:00 2001 From: "Sk.Lv" Date: Tue, 26 Nov 2024 15:14:34 +0800 Subject: [PATCH 3/5] feat: ignore tags --- ksyun/service_ksyun_vpc.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ksyun/service_ksyun_vpc.go b/ksyun/service_ksyun_vpc.go index ba851764..beef088a 100644 --- a/ksyun/service_ksyun_vpc.go +++ b/ksyun/service_ksyun_vpc.go @@ -1147,6 +1147,7 @@ func (s *VpcService) CreateNat(d *schema.ResourceData, r *schema.Resource) (err func (s *VpcService) ModifyNatCall(d *schema.ResourceData, r *schema.Resource) (callback ApiCall, err error) { transform := map[string]SdkReqTransform{ "project_id": {Ignore: true}, + "tags": {Ignore: true}, } req, err := SdkRequestAutoMapping(d, r, true, transform, nil, SdkReqParameter{false}) if err != nil { From b639e5ad91488724185bb55b4b71278a82109780 Mon Sep 17 00:00:00 2001 From: notone <42347556+notone0010@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:48:52 +0800 Subject: [PATCH 4/5] Update CHANGELOG.md --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1e5aee5..f4dfd295 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 1.17.3 (Nov 26, 2024) + +IMPROVEMENTS: + +- `ksyun_instance`: 新增ebs tag同步,优化data_disks管理 +- `ksyun_nat`: 新增tags嵌入管理 +- `ksyun_alb`: 新增tags嵌入管理 +- `ksyun_ebs`: 新增tags嵌入管理 +- `ksyun_bws`: 新增tags嵌入管理 + ## 1.17.2 (Nov 22, 2024) BUGFIX: From 6db959291d930762f6cf926d26b6f8d83745934e Mon Sep 17 00:00:00 2001 From: fengyikai Date: Fri, 13 Dec 2024 13:58:29 +0800 Subject: [PATCH 5/5] ksyun iam relation policy --- CHANGELOG.md | 6 + ksyun/provider.go | 10 +- ksyun/resource_ksyun_iam_relation_policy.go | 94 +++++ ...resource_ksyun_iam_relation_policy_test.go | 30 ++ ksyun/service_ksyun_iam_relation_policy.go | 337 ++++++++++++++++++ .../docs/r/iam_relation_policy.html.markdown | 55 +++ website/ksyun.erb | 3 + 7 files changed, 531 insertions(+), 4 deletions(-) create mode 100644 ksyun/resource_ksyun_iam_relation_policy.go create mode 100644 ksyun/resource_ksyun_iam_relation_policy_test.go create mode 100644 ksyun/service_ksyun_iam_relation_policy.go create mode 100644 website/docs/r/iam_relation_policy.html.markdown diff --git a/CHANGELOG.md b/CHANGELOG.md index f4dfd295..6e24464f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.17.4(Dec 13, 2024) + +FEATURES: + +- - **New Resource:** `ksyun_iam_relation_policy` IAM策略关联 + ## 1.17.3 (Nov 26, 2024) IMPROVEMENTS: diff --git a/ksyun/provider.go b/ksyun/provider.go index 2170437f..5867a367 100644 --- a/ksyun/provider.go +++ b/ksyun/provider.go @@ -330,6 +330,7 @@ IAM ksyun_iam_role ksyun_iam_group ksyun_iam_policy + ksyun_iam_relation_policy */ package ksyun @@ -609,10 +610,11 @@ func Provider() terraform.ResourceProvider { "ksyun_tag_v2_attachment": resourceKsyunTagv2Attachment(), // iam - "ksyun_iam_user": resourceKsyunIamUser(), - "ksyun_iam_role": resourceKsyunIamRole(), - "ksyun_iam_group": resourceKsyunIamGroup(), - "ksyun_iam_policy": resourceKsyunIamPolicy(), + "ksyun_iam_user": resourceKsyunIamUser(), + "ksyun_iam_role": resourceKsyunIamRole(), + "ksyun_iam_group": resourceKsyunIamGroup(), + "ksyun_iam_policy": resourceKsyunIamPolicy(), + "ksyun_iam_relation_policy": resourceKsyunIamRelationPolicy(), // security group "ksyun_security_group": resourceKsyunSecurityGroup(), diff --git a/ksyun/resource_ksyun_iam_relation_policy.go b/ksyun/resource_ksyun_iam_relation_policy.go new file mode 100644 index 00000000..fbe08f33 --- /dev/null +++ b/ksyun/resource_ksyun_iam_relation_policy.go @@ -0,0 +1,94 @@ +/* +Provides a Iam Policy resource. + +# Example Usage + +```hcl +resource "ksyun_iam_relation_policy" "user" { + name = "iam_user_name" + policy_name = "IAMReadOnlyAccess" + relation_type = 1 +}` + +resource "ksyun_iam_relation_policy" "user" { + name = "iam_role_name" + policy_name = "IAMReadOnlyAccess" + relation_type = 2 +}` + +``` + +# Import + +IAM Policy can be imported using the `policy_name`, e.g. + +``` +$ terraform import ksyun_iam_relation_policy.user +``` +*/ + +package ksyun + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceKsyunIamRelationPolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceKsyunIamRelationPolicyCreate, + Read: resourceKsyunIamRelationPolicyRead, + Delete: resourceKsyunIamRelationPolicyDelete, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "IAM UserName or RoleName according to relation type.", + }, + "policy_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "IAM PolicyName.", + }, + "relation_type": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + Description: "relation type 1 is the user,relation type 2 is the role.", + }, + }, + } +} + +func resourceKsyunIamRelationPolicyCreate(d *schema.ResourceData, meta interface{}) (err error) { + iamRelationPolicyService := IamRelationPolicyService{meta.(*KsyunClient)} + err = iamRelationPolicyService.CreateIamRelationPolicy(d, resourceKsyunIamRelationPolicy()) + if err != nil { + return fmt.Errorf("error on creating IAM reliaton policy %q, %s", d.Id(), err) + } + return +} + +func resourceKsyunIamRelationPolicyUpdate(d *schema.ResourceData, meta interface{}) (err error) { + return +} + +func resourceKsyunIamRelationPolicyRead(d *schema.ResourceData, meta interface{}) (err error) { + iamRelationPolicyService := IamRelationPolicyService{meta.(*KsyunClient)} + err = iamRelationPolicyService.ReadAndSetIamRelationPolicy(d, resourceKsyunIamRelationPolicy()) + if err != nil { + return fmt.Errorf("error on reading IAM reliaton policy, %s", err) + } + return +} + +func resourceKsyunIamRelationPolicyDelete(d *schema.ResourceData, meta interface{}) (err error) { + iamRelationPolicyService := IamRelationPolicyService{meta.(*KsyunClient)} + err = iamRelationPolicyService.DeleteIamRelationPolicy(d) + if err != nil { + return fmt.Errorf("error on deleting IAM reliaton policy %q, %s", d.Id(), err) + } + return +} diff --git a/ksyun/resource_ksyun_iam_relation_policy_test.go b/ksyun/resource_ksyun_iam_relation_policy_test.go new file mode 100644 index 00000000..b293bc75 --- /dev/null +++ b/ksyun/resource_ksyun_iam_relation_policy_test.go @@ -0,0 +1,30 @@ +package ksyun + +import ( + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "testing" +) + +func TestAccKsyunIamRelationPolicy_basic(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccKsyunIamRelationPolicyConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckIDExists("ksyun_iam_relation_policy.user"), + ), + }, + }, + }) +} + +const testAccKsyunIamRelationPolicyConfig = ` +resource "ksyun_iam_relation_policy" "user" { + name = "username01" + policy_name = "IAMReadOnlyAccess" + relation_type = 1 +}` diff --git a/ksyun/service_ksyun_iam_relation_policy.go b/ksyun/service_ksyun_iam_relation_policy.go new file mode 100644 index 00000000..80461ba1 --- /dev/null +++ b/ksyun/service_ksyun_iam_relation_policy.go @@ -0,0 +1,337 @@ +package ksyun + +import ( + "context" + "encoding/json" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/terraform-providers/terraform-provider-ksyun/logger" + "time" +) + +type IamRelationPolicyService struct { + client *KsyunClient +} + +type ListAttachedUserPoliciesResult struct { + ListAttachedUserPoliciesResult struct { + AttachedPolicies struct { + Member []ListAttachedUserOrRolePoliciesMembersResult `json:"member,omitempty"` + } `json:"AttachedPolicies,omitempty"` + IsTruncated bool `json:"IsTruncated,omitempty"` + Total int `json:"Total,omitempty"` + } `json:"ListAttachedUserPoliciesResult,omitempty"` + RequestId string `json:"RequestId,omitempty"` +} + +type ListAttachedRolePoliciesResult struct { + ListAttachedRolePoliciesResult struct { + AttachedPolicies struct { + Member []ListAttachedUserOrRolePoliciesMembersResult `json:"member,omitempty"` + } `json:"AttachedPolicies,omitempty"` + IsTruncated bool `json:"IsTruncated,omitempty"` + Marker string `json:"Marker,omitempty"` + } `json:"ListAttachedRolePoliciesResult,omitempty"` + RequestId string `json:"RequestId,omitempty"` +} + +type ListAttachedUserOrRolePoliciesMembersResult struct { + PolicyKrn string `json:"PolicyKrn,omitempty"` + PolicyName string `json:"PolicyName,omitempty"` + CreateTime string `json:"CreateTime,omitempty"` + Description string `json:"Description,omitempty"` + DescriptionEn string `json:"Description_en,omitempty"` + Type int `json:"Type,omitempty"` +} + +func (s *IamRelationPolicyService) CreateIAMRelationPolicyCommonCall(req map[string]interface{}, isSetId bool) (callback ApiCall, err error) { + if req["RelationType"] == 1 { + sendParams := map[string]interface{}{} + sendParams["UserName"] = req["Name"] + sendParams["PolicyKrn"] = fmt.Sprintf("krn:ksc:iam::ksc:policy/%s", req["PolicyName"]) + + callback = ApiCall{ + param: &sendParams, + action: "AttachUserPolicy", + executeCall: func(d *schema.ResourceData, client *KsyunClient, call ApiCall) (resp *map[string]interface{}, err error) { + conn := client.iamconn + logger.Debug(logger.RespFormat, call.action, *(call.param)) + resp, err = conn.AttachUserPolicy(call.param) + if err == nil { + d.SetId(fmt.Sprintf("%s-%s", sendParams["UserName"], sendParams["PolicyKrn"])) + } + return resp, err + }, + afterCall: func(d *schema.ResourceData, client *KsyunClient, resp *map[string]interface{}, call ApiCall) (err error) { + logger.Debug(logger.RespFormat, call.action, *(call.param), *resp) + err = d.Set("relation_type", 1) + if err != nil { + return err + } + err = d.Set("name", sendParams["UserName"]) + if err != nil { + return err + } + err = d.Set("policy_name", req["PolicyName"]) + if err != nil { + return err + } + return err + }, + } + return callback, err + } else { + sendParams := map[string]interface{}{} + sendParams["RoleName"] = req["Name"] + sendParams["PolicyKrn"] = fmt.Sprintf("krn:ksc:iam::ksc:policy/%s", req["PolicyName"]) + + callback = ApiCall{ + param: &sendParams, + action: "AttachRolePolicy", + executeCall: func(d *schema.ResourceData, client *KsyunClient, call ApiCall) (resp *map[string]interface{}, err error) { + conn := client.iamconn + logger.Debug(logger.RespFormat, call.action, *(call.param)) + resp, err = conn.AttachRolePolicy(call.param) + if err == nil { + d.SetId(fmt.Sprintf("%s-%s", sendParams["RoleName"], sendParams["PolicyKrn"])) + } + return resp, err + }, + afterCall: func(d *schema.ResourceData, client *KsyunClient, resp *map[string]interface{}, call ApiCall) (err error) { + logger.Debug(logger.RespFormat, call.action, *(call.param), *resp) + err = d.Set("relation_type", 2) + if err != nil { + return err + } + err = d.Set("name", sendParams["RoleName"]) + if err != nil { + return err + } + err = d.Set("policy_name", req["PolicyName"]) + if err != nil { + return err + } + return err + }, + } + return callback, err + } +} + +func (s *IamRelationPolicyService) CreateIamRelationPolicy(d *schema.ResourceData, r *schema.Resource) error { + apiProcess := NewApiProcess(context.Background(), d, s.client, false) + + createPolicyCall, err := s.CreateIamRelationPolicyCall(d, r) + if err != nil { + return err + } + apiProcess.PutCalls(createPolicyCall) + return apiProcess.Run() +} + +func (s *IamRelationPolicyService) CreateIamRelationPolicyCall(d *schema.ResourceData, r *schema.Resource) (callback ApiCall, err error) { + req, err := SdkRequestAutoMapping(d, r, false, nil, nil) + if err != nil { + return callback, err + } + return s.CreateIAMRelationPolicyCommonCall(req, false) +} + +func (s *IamRelationPolicyService) DeleteIamRelationPolicy(d *schema.ResourceData) error { + apiProcess := NewApiProcess(context.Background(), d, s.client, true) + + deleteCall, err := s.DeleteIamRelationPolicyCall(d) + if err != nil { + return err + } + apiProcess.PutCalls(deleteCall) + + return apiProcess.Run() +} + +func (s *IamRelationPolicyService) DeleteIamRelationPolicyCall(d *schema.ResourceData) (callback ApiCall, err error) { + relationType := d.Get("relation_type").(int) + if relationType == 1 { + // 构成参数 + params := map[string]interface{}{} + params["UserName"] = d.Get("name").(string) + params["PolicyKrn"] = fmt.Sprintf("krn:ksc:iam::ksc:policy/%s", d.Get("policy_name").(string)) + + callback = ApiCall{ + param: ¶ms, + action: "DetachUserPolicy", + executeCall: func(d *schema.ResourceData, client *KsyunClient, call ApiCall) (resp *map[string]interface{}, err error) { + conn := client.iamconn + logger.Debug(logger.RespFormat, call.action, *(call.param)) + resp, err = conn.DetachUserPolicy(call.param) + return resp, err + }, + callError: func(d *schema.ResourceData, client *KsyunClient, call ApiCall, baseErr error) error { + return resource.Retry(5*time.Minute, func() *resource.RetryError { + if notFoundError(baseErr) { + return nil + } + + // it cannot be deleted if this is still using + if isExpectError(baseErr, []string{ + "PolicyNoSuchEntity", + "UserNoSuchEntity", + "UserPolicyNoSuchEntity", + }) { + return resource.NonRetryableError(baseErr) + } + return resource.RetryableError(baseErr) + }) + }, + afterCall: func(d *schema.ResourceData, client *KsyunClient, resp *map[string]interface{}, call ApiCall) (err error) { + logger.Debug(logger.RespFormat, call.action, *(call.param), *resp) + return err + }, + } + } else { + // 构成参数 + params := map[string]interface{}{} + params["RoleName"] = d.Get("name").(string) + params["PolicyKrn"] = fmt.Sprintf("krn:ksc:iam::ksc:policy/%s", d.Get("policy_name").(string)) + + callback = ApiCall{ + param: ¶ms, + action: "DetachRolePolicy", + executeCall: func(d *schema.ResourceData, client *KsyunClient, call ApiCall) (resp *map[string]interface{}, err error) { + conn := client.iamconn + logger.Debug(logger.RespFormat, call.action, *(call.param)) + resp, err = conn.DetachRolePolicy(call.param) + return resp, err + }, + callError: func(d *schema.ResourceData, client *KsyunClient, call ApiCall, baseErr error) error { + return resource.Retry(5*time.Minute, func() *resource.RetryError { + if notFoundError(baseErr) { + return nil + } + + // it cannot be deleted if this is still using + if isExpectError(baseErr, []string{ + "PolicyNoSuchEntity", + "RoleNoSuchEntity", + "RolePolicyNoSuchEntity", + }) { + return resource.NonRetryableError(baseErr) + } + return resource.RetryableError(baseErr) + }) + }, + afterCall: func(d *schema.ResourceData, client *KsyunClient, resp *map[string]interface{}, call ApiCall) (err error) { + logger.Debug(logger.RespFormat, call.action, *(call.param), *resp) + return err + }, + } + } + return +} + +func (s *IamRelationPolicyService) ReadAndSetIamRelationPolicy(d *schema.ResourceData, r *schema.Resource) (err error) { + relationType := d.Get("relation_type").(int) + name := d.Get("name").(string) + policyName := d.Get("policy_name").(string) + + var data []interface{} + data, err = s.ReadRelationPolicy(relationType, name, policyName) + SdkResponseAutoResourceData(d, r, data, nil) + + return +} + +func (s *IamRelationPolicyService) ReadRelationPolicy(relationType int, name string, policyName string) (data []interface{}, err error) { + var resp *map[string]interface{} + + var policyMemberResult ListAttachedUserOrRolePoliciesMembersResult + if relationType == 1 { + var condition = map[string]interface{}{} + page, MaxItems := 1, 1 + for { + condition["UserName"] = name + condition["Page"] = page + condition["MaxItems"] = MaxItems + conn := s.client.iamconn + action := "ListAttachedUserPolicies" + logger.Debug(logger.ReqFormat, action, condition) + resp, err = conn.ListAttachedUserPolicies(&condition) + if err != nil { + return data, err + } + respJson, err := json.Marshal(resp) + if err != nil { + return data, err + } + var result ListAttachedUserPoliciesResult + err = json.Unmarshal(respJson, &result) + if err != nil { + return data, err + } + if len(result.ListAttachedUserPoliciesResult.AttachedPolicies.Member) <= 0 { + break + } + for _, item := range result.ListAttachedUserPoliciesResult.AttachedPolicies.Member { + if item.PolicyName == policyName { + policyMemberResult = item + break + } + } + if !result.ListAttachedUserPoliciesResult.IsTruncated { + break + } + page++ + } + } else { + var condition = map[string]interface{}{} + marker, MaxItems := "", 1 + for { + condition["RoleName"] = name + condition["Marker"] = marker + condition["MaxItems"] = MaxItems + conn := s.client.iamconn + action := "ListAttachedRolePolicies" + logger.Debug(logger.ReqFormat, action, condition) + resp, err = conn.ListAttachedRolePolicies(&condition) + if err != nil { + return data, err + } + respJson, err := json.Marshal(resp) + if err != nil { + return data, err + } + var result ListAttachedRolePoliciesResult + err = json.Unmarshal(respJson, &result) + if err != nil { + return data, err + } + if len(result.ListAttachedRolePoliciesResult.AttachedPolicies.Member) <= 0 { + break + } + for _, item := range result.ListAttachedRolePoliciesResult.AttachedPolicies.Member { + if item.PolicyName == policyName { + policyMemberResult = item + break + } + } + if !result.ListAttachedRolePoliciesResult.IsTruncated { + break + } + marker = result.ListAttachedRolePoliciesResult.Marker + } + } + + if policyMemberResult.PolicyName != "" { + data = append(data, map[string]interface{}{ + "PolicyKrn": policyMemberResult.PolicyKrn, + "PolicyName": policyMemberResult.PolicyName, + "CreateTime": policyMemberResult.CreateTime, + "Description": policyMemberResult.Description, + "DescriptionEn": policyMemberResult.DescriptionEn, + "Type": policyMemberResult.Type, + }) + } + + return data, err +} diff --git a/website/docs/r/iam_relation_policy.html.markdown b/website/docs/r/iam_relation_policy.html.markdown new file mode 100644 index 00000000..e777c390 --- /dev/null +++ b/website/docs/r/iam_relation_policy.html.markdown @@ -0,0 +1,55 @@ +--- +subcategory: "IAM" +layout: "ksyun" +page_title: "ksyun: ksyun_iam_relation_policy" +sidebar_current: "docs-ksyun-resource-iam_relation_policy" +description: |- + Provides a Iam Policy resource. +--- + +# ksyun_iam_relation_policy + +Provides a Iam Policy resource. + +# + +## Example Usage + +```hcl +resource "ksyun_iam_relation_policy" "user" { + name = "iam_user_name" + policy_name = "IAMReadOnlyAccess" + relation_type = 1 +} ` + +resource "ksyun_iam_relation_policy" "user" { + name = "iam_role_name" + policy_name = "IAMReadOnlyAccess" + relation_type = 2 +} ` +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required, ForceNew) IAM UserName or RoleName according to relation type. +* `policy_name` - (Required, ForceNew) IAM PolicyName. +* `relation_type` - (Required, ForceNew) relation type 1 is the user,relation type 2 is the role. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - ID of the resource. + + + +## Import + +IAM Policy can be imported using the `policy_name`, e.g. + +``` +$ terraform import ksyun_iam_relation_policy.user +``` + diff --git a/website/ksyun.erb b/website/ksyun.erb index e7568329..e57577f4 100644 --- a/website/ksyun.erb +++ b/website/ksyun.erb @@ -218,6 +218,9 @@
  • ksyun_iam_policy
  • +
  • + ksyun_iam_relation_policy +