From 25da11a973f690b6acc6d3be84fadbf000ae1e76 Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Tue, 20 Aug 2024 15:13:56 +0200 Subject: [PATCH 01/15] Deprecated ibm_resource_access_tag in favor of ibm_iam_access_tag --- ibm/provider/provider.go | 4 + .../resource_ibm_iam_access_tag.go | 165 ++++++++++++++++++ .../resource_ibm_iam_access_tag_test.go | 142 +++++++++++++++ .../resource_ibm_resource_access_tag.go | 1 + website/docs/r/iam_access_tag.html.markdown | 66 +++++++ .../docs/r/resource_access_tag.html.markdown | 3 + 6 files changed, 381 insertions(+) create mode 100644 ibm/service/globaltagging/resource_ibm_iam_access_tag.go create mode 100644 ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go create mode 100644 website/docs/r/iam_access_tag.html.markdown diff --git a/ibm/provider/provider.go b/ibm/provider/provider.go index 8dccade6f8..59398ca507 100644 --- a/ibm/provider/provider.go +++ b/ibm/provider/provider.go @@ -1395,6 +1395,9 @@ func Provider() *schema.Provider { "ibm_resource_tag": globaltagging.ResourceIBMResourceTag(), "ibm_resource_access_tag": globaltagging.ResourceIBMResourceAccessTag(), + // Added for Iam Access Tag + "ibm_iam_access_tag": globaltagging.ResourceIBMIamAccessTag(), + // Atracker "ibm_atracker_target": atracker.ResourceIBMAtrackerTarget(), "ibm_atracker_route": atracker.ResourceIBMAtrackerRoute(), @@ -1863,6 +1866,7 @@ func Validator() validate.ValidatorDict { "ibm_is_virtual_endpoint_gateway": vpc.ResourceIBMISEndpointGatewayValidator(), "ibm_resource_tag": globaltagging.ResourceIBMResourceTagValidator(), "ibm_resource_access_tag": globaltagging.ResourceIBMResourceAccessTagValidator(), + "ibm_iam_access_tag": globaltagging.ResourceIBMIamAccessTagValidator(), "ibm_satellite_location": satellite.ResourceIBMSatelliteLocationValidator(), "ibm_satellite_cluster": satellite.ResourceIBMSatelliteClusterValidator(), "ibm_pi_volume": power.ResourceIBMPIVolumeValidator(), diff --git a/ibm/service/globaltagging/resource_ibm_iam_access_tag.go b/ibm/service/globaltagging/resource_ibm_iam_access_tag.go new file mode 100644 index 0000000000..37b365828e --- /dev/null +++ b/ibm/service/globaltagging/resource_ibm_iam_access_tag.go @@ -0,0 +1,165 @@ +// Copyright IBM Corp. 2017, 2024 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +package globaltagging + +import ( + "encoding/json" + "fmt" + "log" + + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/validate" + "github.com/IBM/platform-services-go-sdk/globaltaggingv1" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func ResourceIBMIamAccessTag() *schema.Resource { + return &schema.Resource{ + Create: resourceIBMIamAccessTagCreate, + Read: resourceIBMIamAccessTagRead, + Delete: resourceIBMIamAccessTagDelete, + Importer: &schema.ResourceImporter{}, + + Schema: map[string]*schema.Schema{ + + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.InvokeValidator("ibm_iam_access_tag", "name"), + Set: flex.ResourceIBMVPCHash, + Description: "Name of the access tag", + }, + tagType: { + Type: schema.TypeString, + Computed: true, + Description: "Type of the tag(access)", + }, + }, + } +} + +func ResourceIBMIamAccessTagValidator() *validate.ResourceValidator { + + validateSchema := make([]validate.ValidateSchema, 0) + + validateSchema = append(validateSchema, + validate.ValidateSchema{ + Identifier: "name", + ValidateFunctionIdentifier: validate.ValidateRegexpLen, + Type: validate.TypeString, + Required: true, + Regexp: `^([A-Za-z0-9_.-]|[A-Za-z0-9_.-][A-Za-z0-9_ .-]*[A-Za-z0-9_.-]):([A-Za-z0-9_.-]|[A-Za-z0-9_.-][A-Za-z0-9_ .-]*[A-Za-z0-9_.-])$`, + MinValueLength: 1, + MaxValueLength: 128}) + + ibmIamAccessTagValidator := validate.ResourceValidator{ResourceName: "ibm_iam_access_tag", Schema: validateSchema} + return &ibmIamAccessTagValidator +} + +func resourceIBMIamAccessTagCreate(d *schema.ResourceData, meta interface{}) error { + + gtClient, err := meta.(conns.ClientSession).GlobalTaggingAPIv1() + if err != nil { + return fmt.Errorf("Error getting global tagging client settings: %s", err) + } + + tagName := d.Get("name").(string) + add := make([]string, 0) + add = append(add, tagName) + accessTagType := "access" + createTagOptions := &globaltaggingv1.CreateTagOptions{ + TagType: &accessTagType, + TagNames: add, + } + results, _, err := gtClient.CreateTag(createTagOptions) + if err != nil { + return err + } + if results != nil { + errMap := make([]globaltaggingv1.CreateTagResultsResultsItem, 0) + for _, res := range results.Results { + if res.IsError != nil && *res.IsError { + errMap = append(errMap, res) + } + } + if len(errMap) > 0 { + output, err := json.MarshalIndent(errMap, "", " ") + log.Printf("err is %s", err) + return fmt.Errorf("[ERROR] Error while creating access tag(%s) : %s", tagName, string(output)) + } + } + + d.SetId(tagName) + d.Set(tagType, accessTagType) + + return nil +} + +func resourceIBMIamAccessTagRead(d *schema.ResourceData, meta interface{}) error { + tagName := d.Id() + gtClient, err := meta.(conns.ClientSession).GlobalTaggingAPIv1() + if err != nil { + return fmt.Errorf("Error getting global tagging client settings: %s", err) + } + accessTagType := "access" + listTagsOptions := &globaltaggingv1.ListTagsOptions{ + TagType: &accessTagType, + } + taggingResult, _, err := gtClient.ListTags(listTagsOptions) + if err != nil { + return err + } + + var taglist []string + for _, item := range taggingResult.Items { + taglist = append(taglist, *item.Name) + } + existingAccessTags := flex.NewStringSet(flex.ResourceIBMVPCHash, taglist) + if !existingAccessTags.Contains(tagName) { + d.SetId("") + return nil + } + d.Set("name", tagName) + d.Set(tagType, accessTagType) + return nil +} + +func resourceIBMIamAccessTagDelete(d *schema.ResourceData, meta interface{}) error { + + gtClient, err := meta.(conns.ClientSession).GlobalTaggingAPIv1() + if err != nil { + return fmt.Errorf("[ERROR] Error getting global tagging client settings: %s", err) + } + tagName := d.Get("name").(string) + accessTagType := "access" + + deleteTagOptions := &globaltaggingv1.DeleteTagOptions{ + TagName: &tagName, + TagType: &accessTagType, + } + + results, resp, err := gtClient.DeleteTag(deleteTagOptions) + + if err != nil { + return fmt.Errorf("[ERROR] Error while deleting access tag(%s) : %v\n%v", tagName, err, resp) + } + if results != nil { + errMap := make([]globaltaggingv1.DeleteTagResultsItem, 0) + for _, res := range results.Results { + if res.IsError != nil && *res.IsError { + errMap = append(errMap, res) + } + } + if len(errMap) > 0 { + output, err := json.MarshalIndent(errMap, "", " ") + log.Printf("err is %s", err) + return fmt.Errorf("[ERROR] Error while deleting access tag(%s) : %s", tagName, string(output)) + } + } + + d.SetId("") + return nil +} diff --git a/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go b/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go new file mode 100644 index 0000000000..a35f0c2f0e --- /dev/null +++ b/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go @@ -0,0 +1,142 @@ +// Copyright IBM Corp. 2017, 2024 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +package globaltagging_test + +import ( + "fmt" + "regexp" + "strings" + "testing" + + acc "github.com/IBM-Cloud/terraform-provider-ibm/ibm/acctest" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" + "github.com/IBM/platform-services-go-sdk/globaltaggingv1" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +const ( + iamAccessTagRegex = "^([A-Za-z0-9_.-]|[A-Za-z0-9_.-][A-Za-z0-9_ .-]*[A-Za-z0-9_.-]):([A-Za-z0-9_.-]|[A-Za-z0-9_.-][A-Za-z0-9_ .-]*[A-Za-z0-9_.-])$" +) + +func TestAccIamAccessTag_Basic(t *testing.T) { + name := fmt.Sprintf("tf%d:iam-access%d", acctest.RandIntRange(10, 100), acctest.RandIntRange(10, 100)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + + resource.TestStep{ + Config: testAccCheckIamAccessTagCreate(name), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIamAccessTagExists("ibm_iam_access_tag.tag"), + resource.TestCheckResourceAttr("ibm_iam_access_tag.tag", "id", name), + resource.TestCheckResourceAttr("ibm_iam_access_tag.tag", "name", name), + resource.TestCheckResourceAttr("ibm_iam_access_tag.tag", "tag_type", "access"), + ), + }, + }, + }) +} +func TestAccIamAccessTag_Usage(t *testing.T) { + name := fmt.Sprintf("tf%d:iam-access%d", acctest.RandIntRange(10, 100), acctest.RandIntRange(10, 100)) + publicKey := strings.TrimSpace(` +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR +`) + sshkeyname := fmt.Sprintf("tfssh-createname-%d", acctest.RandIntRange(10, 100)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + + resource.TestStep{ + Config: testAccCheckIamAccessTagCreate(name), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIamAccessTagExists("ibm_iam_access_tag.tag"), + resource.TestCheckResourceAttr("ibm_iam_access_tag.tag", "id", name), + resource.TestCheckResourceAttr("ibm_iam_access_tag.tag", "name", name), + resource.TestCheckResourceAttr("ibm_iam_access_tag.tag", "tag_type", "access"), + ), + }, + resource.TestStep{ + Config: testAccCheckIamAccessTagUsage(name, sshkeyname, publicKey), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIamAccessTagExists("ibm_iam_access_tag.tag"), + resource.TestCheckResourceAttr("ibm_iam_access_tag.tag", "id", name), + resource.TestCheckResourceAttr("ibm_iam_access_tag.tag", "name", name), + resource.TestCheckResourceAttr("ibm_iam_access_tag.tag", "tag_type", "access"), + resource.TestCheckResourceAttr("ibm_is_ssh_key.key", "name", sshkeyname), + resource.TestCheckResourceAttrSet("ibm_is_ssh_key.key", "access_tags.#"), + resource.TestCheckResourceAttr("ibm_is_ssh_key.key", "access_tags.0", name), + ), + }, + }, + }) +} + +func testAccCheckIamAccessTagExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + var tagName string + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + iamAccessTagRegex, err := regexp.Compile(iamAccessTagRegex) + if err != nil { + return err + } + + if iamAccessTagRegex.MatchString(rs.Primary.ID) { + tagName = rs.Primary.ID + } + + gtClient, err := acc.TestAccProvider.Meta().(conns.ClientSession).GlobalTaggingAPIv1() + if err != nil { + return fmt.Errorf("Error getting global tagging client settings: %s", err) + } + accessTagType := "access" + listTagsOptions := &globaltaggingv1.ListTagsOptions{ + TagType: &accessTagType, + } + taggingResult, _, err := gtClient.ListTags(listTagsOptions) + if err != nil { + return err + } + + var taglist []string + for _, item := range taggingResult.Items { + taglist = append(taglist, *item.Name) + } + existingAccessTags := flex.NewStringSet(flex.ResourceIBMVPCHash, taglist) + if !existingAccessTags.Contains(tagName) { + return fmt.Errorf( + "Error on get of resource tags (%s) : %s", tagName, err) + } + return nil + } +} + +func testAccCheckIamAccessTagCreate(name string) string { + return fmt.Sprintf(` + resource ibm_iam_access_tag tag { + name = "%s" + } +`, name) +} +func testAccCheckIamAccessTagUsage(name, sshkeyname, publicKey string) string { + return fmt.Sprintf(` + resource ibm_iam_access_tag tag { + name = "%s" + } + resource "ibm_is_ssh_key" "key" { + name = "%s" + public_key = "%s" + access_tags = [ibm_iam_access_tag.tag.name] + } +`, name, sshkeyname, publicKey) +} diff --git a/ibm/service/globaltagging/resource_ibm_resource_access_tag.go b/ibm/service/globaltagging/resource_ibm_resource_access_tag.go index f4d1d5b9d2..ad5d3d39a1 100644 --- a/ibm/service/globaltagging/resource_ibm_resource_access_tag.go +++ b/ibm/service/globaltagging/resource_ibm_resource_access_tag.go @@ -38,6 +38,7 @@ func ResourceIBMResourceAccessTag() *schema.Resource { Description: "Type of the tag(access)", }, }, + DeprecationMessage: "ibm_resource_access_tag has been deprecated. Use ibm_iam_access_tag instead.", } } diff --git a/website/docs/r/iam_access_tag.html.markdown b/website/docs/r/iam_access_tag.html.markdown new file mode 100644 index 0000000000..656223531a --- /dev/null +++ b/website/docs/r/iam_access_tag.html.markdown @@ -0,0 +1,66 @@ +--- +subcategory: "Global Tagging" +layout: "ibm" +page_title: "IBM : iam_access_tag" +description: |- + Manages iam access tags. +--- + +# ibm_iam_access_tag + +Create or delete IBM Cloud access management tags. For more information, about access tags, see [IBM Cloud access management tags](https://cloud.ibm.com/apidocs/tagging#create-tag). + + +## Example usage +The following example enables you to create access management tags + +```terraform +resource "ibm_iam_access_tag" "example" { + name = "example:tag" +} + +``` + +## Argument reference +Review the argument references that you can specify for your resource. + +- `name` - (Required, String) The name of the access management tag. + + +## Attributes reference +In addition to all argument reference list, you can access the following attribute reference after your resource is created. + +- `id` - (String) The unique identifier of the access tag. Same as `name`. +- `tag_type` - (String) Type of the tag(`access`) + + +## Import + +The `ibm_iam_access_tag` resource can be imported by using the resource CRN. + +**Syntax** + +``` +$ terraform import ibm_iam_access_tag.tag tag_name +``` + +**Example** + +``` +$ terraform import ibm_iam_access_tag.tag crn:v1:bluemix:public:satellite:us-east:a/ab3ed67929c2a81285fbb5f9eb22800a:c1ga7h9w0angomd44654:: + +``` + +Example for importing access tags. + +**Syntax** + +``` +$ terraform import ibm_iam_access_tag.tag tag_name +``` + +**Example** + +``` +$ terraform import ibm_iam_access_tag.tag example:test +``` diff --git a/website/docs/r/resource_access_tag.html.markdown b/website/docs/r/resource_access_tag.html.markdown index 5500d5105f..8917eae134 100644 --- a/website/docs/r/resource_access_tag.html.markdown +++ b/website/docs/r/resource_access_tag.html.markdown @@ -8,6 +8,9 @@ description: |- # ibm_resource_access_tag + ~>**Deprecated:** + The ability to use the ibm_resource_access_tag resource to create or delete IBM Cloud access management tags in Terraform has been removed in favor of a dedicated ibm_iam_access_tag resource. For more information, check out [here](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/iam_access_tag) + Create, update, or delete IBM Cloud access management tags. For more information, about tagging, see [IBM Cloud access management tags](https://cloud.ibm.com/apidocs/tagging#create-tag). From 1e6ed472ff4336dc116e972d10846689a73cb663 Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Tue, 20 Aug 2024 17:17:07 +0200 Subject: [PATCH 02/15] changes --- .../resource_ibm_iam_access_tag_test.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go b/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go index a35f0c2f0e..0f6f459d04 100644 --- a/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go +++ b/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go @@ -69,9 +69,10 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVE resource.TestCheckResourceAttr("ibm_iam_access_tag.tag", "id", name), resource.TestCheckResourceAttr("ibm_iam_access_tag.tag", "name", name), resource.TestCheckResourceAttr("ibm_iam_access_tag.tag", "tag_type", "access"), - resource.TestCheckResourceAttr("ibm_is_ssh_key.key", "name", sshkeyname), - resource.TestCheckResourceAttrSet("ibm_is_ssh_key.key", "access_tags.#"), - resource.TestCheckResourceAttr("ibm_is_ssh_key.key", "access_tags.0", name), + testAccCheckResourceTagExists("ibm_resource_tag.tag"), + resource.TestCheckResourceAttr("ibm_resource_tag.tag", "tags.#", "1"), + resource.TestCheckResourceAttr("ibm_resource_tag.tag", "tags.0", name), + resource.TestCheckResourceAttr("ibm_resource_tag.tag", "tag_type", "access"), ), }, }, @@ -136,7 +137,11 @@ func testAccCheckIamAccessTagUsage(name, sshkeyname, publicKey string) string { resource "ibm_is_ssh_key" "key" { name = "%s" public_key = "%s" - access_tags = [ibm_iam_access_tag.tag.name] + } + resource "ibm_resource_tag" "tag" { + resource_id = ibm_is_ssh_key.key.crn + tags = [ibm_iam_access_tag.tag.name] + tag_type = "access" } `, name, sshkeyname, publicKey) } From 57c9d159303a04091ba2ddad6553004922412a63 Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Wed, 21 Aug 2024 11:56:29 +0200 Subject: [PATCH 03/15] Fix --- ibm/service/globaltagging/resource_ibm_resource_tag.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ibm/service/globaltagging/resource_ibm_resource_tag.go b/ibm/service/globaltagging/resource_ibm_resource_tag.go index 4db41d51b1..34df86fc29 100644 --- a/ibm/service/globaltagging/resource_ibm_resource_tag.go +++ b/ibm/service/globaltagging/resource_ibm_resource_tag.go @@ -304,6 +304,12 @@ func resourceIBMResourceTagDelete(d *schema.ResourceData, meta interface{}) erro for i, v := range removeTags.List() { remove[i] = fmt.Sprint(v) } + var tType string + if v, ok := d.GetOk(tagType); ok && v != nil { + tType = v.(string) + } else { + tType = "user" + } if len(remove) > 0 { resources := []globaltaggingv1.Resource{} @@ -313,6 +319,7 @@ func resourceIBMResourceTagDelete(d *schema.ResourceData, meta interface{}) erro detachTagOptions := &globaltaggingv1.DetachTagOptions{ Resources: resources, TagNames: remove, + TagType: &tType, } _, resp, err := gtClient.DetachTag(detachTagOptions) From 51a0eb46a5e9b4caf56058f38bd3b0ecb64471b6 Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Wed, 21 Aug 2024 12:27:41 +0200 Subject: [PATCH 04/15] Changed resource to speed up tests --- .../resource_ibm_resource_tag_test.go | 47 ++++++++----------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/ibm/service/globaltagging/resource_ibm_resource_tag_test.go b/ibm/service/globaltagging/resource_ibm_resource_tag_test.go index 38b076b209..b7dca8d395 100644 --- a/ibm/service/globaltagging/resource_ibm_resource_tag_test.go +++ b/ibm/service/globaltagging/resource_ibm_resource_tag_test.go @@ -17,8 +17,7 @@ import ( ) func TestAccResourceTag_Basic(t *testing.T) { - name := fmt.Sprintf("tf-satellitelocation-%d", acctest.RandIntRange(10, 100)) - managed_from := "wdc04" + name := fmt.Sprintf("tf-cos-%d", acctest.RandIntRange(10, 100)) resource.Test(t, resource.TestCase{ PreCheck: func() { acc.TestAccPreCheck(t) }, @@ -26,7 +25,7 @@ func TestAccResourceTag_Basic(t *testing.T) { Steps: []resource.TestStep{ { - Config: testAccCheckResourceTagCreate(name, managed_from), + Config: testAccCheckResourceTagCreate(name), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckResourceTagExists("ibm_resource_tag.tag"), resource.TestCheckResourceAttr("ibm_resource_tag.tag", "tags.#", "2"), @@ -36,12 +35,14 @@ func TestAccResourceTag_Basic(t *testing.T) { ResourceName: "ibm_resource_tag.tag", ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "replace"}, }, }, }) } func TestAccResourceTag_Wait(t *testing.T) { - name := fmt.Sprintf("tf-satellitelocation-%d", acctest.RandIntRange(10, 100)) + name := fmt.Sprintf("tf-cos-%d", acctest.RandIntRange(10, 100)) resource.Test(t, resource.TestCase{ PreCheck: func() { acc.TestAccPreCheck(t) }, @@ -92,40 +93,33 @@ func testAccCheckResourceTagExists(n string) resource.TestCheckFunc { func testAccCheckResourceTagWaitCreate(name string) string { return fmt.Sprintf(` - - resource "ibm_is_vpc" "vpc" { - name = "%s" - } - - data "ibm_is_vpc" "test_vpc" { - name = ibm_is_vpc.vpc.name + resource "ibm_resource_instance" "resource_1" { + name = "%s" + service = "cloud-object-storage" + plan = "lite" + location = "global" } resource "ibm_resource_tag" "tag" { - resource_id = data.ibm_is_vpc.test_vpc.crn + resource_id = ibm_resource_instance.resource_1.crn tags = ["env:dev", "cpu:4", "user:8"] } `, name) } -func testAccCheckResourceTagCreate(name, managed_from string) string { +func testAccCheckResourceTagCreate(name string) string { return fmt.Sprintf(` - - resource "ibm_satellite_location" "location" { - location = "%s" - managed_from = "%s" - description = "satellite service" - zones = ["us-east-1", "us-east-2", "us-east-3"] - } - - data "ibm_satellite_location" "test_location" { - location = ibm_satellite_location.location.id + resource "ibm_resource_instance" "resource_1" { + name = "%s" + service = "cloud-object-storage" + plan = "lite" + location = "global" } resource "ibm_resource_tag" "tag" { - resource_id = data.ibm_satellite_location.test_location.crn + resource_id = ibm_resource_instance.resource_1.crn tags = ["env:dev", "cpu:4"] } -`, name, managed_from) +`, name) } func TestAccResourceTag_replace_Basic(t *testing.T) { @@ -156,14 +150,11 @@ func TestAccResourceTag_replace_Basic(t *testing.T) { func testAccCheckResourceTagCreate_replace(name string) string { return fmt.Sprintf(` - resource "ibm_resource_instance" "resource_1" { name = "%s" service = "cloud-object-storage" plan = "lite" location = "global" - - } resource "ibm_resource_tag" "tag" { From 7d0e02a83c82ed0fd78bf3a2ea4e93fbf88c49b1 Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Wed, 21 Aug 2024 12:30:08 +0200 Subject: [PATCH 05/15] fix --- ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go b/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go index 0f6f459d04..a6d973def4 100644 --- a/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go +++ b/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go @@ -124,14 +124,14 @@ func testAccCheckIamAccessTagExists(n string) resource.TestCheckFunc { func testAccCheckIamAccessTagCreate(name string) string { return fmt.Sprintf(` - resource ibm_iam_access_tag tag { + resource "ibm_iam_access_tag" "tag" { name = "%s" } `, name) } func testAccCheckIamAccessTagUsage(name, sshkeyname, publicKey string) string { return fmt.Sprintf(` - resource ibm_iam_access_tag tag { + resource "ibm_iam_access_tag" "tag" { name = "%s" } resource "ibm_is_ssh_key" "key" { From 5aa7fa21eb0822c9f5012e8c8be13a9f7797a8d6 Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Tue, 3 Sep 2024 15:33:48 +0200 Subject: [PATCH 06/15] PR changes --- website/docs/r/iam_access_tag.html.markdown | 7 +------ website/docs/r/resource_tag.html.markdown | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/website/docs/r/iam_access_tag.html.markdown b/website/docs/r/iam_access_tag.html.markdown index 656223531a..b05642a6ca 100644 --- a/website/docs/r/iam_access_tag.html.markdown +++ b/website/docs/r/iam_access_tag.html.markdown @@ -36,7 +36,7 @@ In addition to all argument reference list, you can access the following attribu ## Import -The `ibm_iam_access_tag` resource can be imported by using the resource CRN. +The `ibm_iam_access_tag` resource can be imported by using the name. **Syntax** @@ -46,11 +46,6 @@ $ terraform import ibm_iam_access_tag.tag tag_name **Example** -``` -$ terraform import ibm_iam_access_tag.tag crn:v1:bluemix:public:satellite:us-east:a/ab3ed67929c2a81285fbb5f9eb22800a:c1ga7h9w0angomd44654:: - -``` - Example for importing access tags. **Syntax** diff --git a/website/docs/r/resource_tag.html.markdown b/website/docs/r/resource_tag.html.markdown index f67194c701..84cc6f229a 100644 --- a/website/docs/r/resource_tag.html.markdown +++ b/website/docs/r/resource_tag.html.markdown @@ -45,7 +45,7 @@ Review the argument references that you can specify for your resource. - `resource_id` - (Required, String) The CRN of the resource on which the tags is be attached. - `resource_type` - (Optional, String) The resource type on which the tags should be attached. -- `tag_type` - (Optional, String) Type of the tag. Supported values are: `user`, `service`, or `access`. The default value is user. +- `tag_type` - (Optional, String) Type of the tag. Supported values are: `user` or `access`. The default value is user. - `tags` - (Required, Array of strings) List of tags associated with resource instance. - `replace` - (Optional, Bool) If true, it indicates that the attaching operation is a replacement operation From 8c4066752750fe707ecd8f6a5489b2ca30dbb596 Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Mon, 9 Sep 2024 17:32:40 +0200 Subject: [PATCH 07/15] PR changes --- .../resource_ibm_iam_access_tag.go | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/ibm/service/globaltagging/resource_ibm_iam_access_tag.go b/ibm/service/globaltagging/resource_ibm_iam_access_tag.go index 37b365828e..fa7874bef7 100644 --- a/ibm/service/globaltagging/resource_ibm_iam_access_tag.go +++ b/ibm/service/globaltagging/resource_ibm_iam_access_tag.go @@ -4,6 +4,7 @@ package globaltagging import ( + "context" "encoding/json" "fmt" "log" @@ -12,15 +13,16 @@ import ( "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/validate" "github.com/IBM/platform-services-go-sdk/globaltaggingv1" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func ResourceIBMIamAccessTag() *schema.Resource { return &schema.Resource{ - Create: resourceIBMIamAccessTagCreate, - Read: resourceIBMIamAccessTagRead, - Delete: resourceIBMIamAccessTagDelete, - Importer: &schema.ResourceImporter{}, + CreateContext: resourceIBMIamAccessTagCreate, + ReadContext: resourceIBMIamAccessTagRead, + DeleteContext: resourceIBMIamAccessTagDelete, + Importer: &schema.ResourceImporter{}, Schema: map[string]*schema.Schema{ @@ -59,11 +61,13 @@ func ResourceIBMIamAccessTagValidator() *validate.ResourceValidator { return &ibmIamAccessTagValidator } -func resourceIBMIamAccessTagCreate(d *schema.ResourceData, meta interface{}) error { +func resourceIBMIamAccessTagCreate(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { gtClient, err := meta.(conns.ClientSession).GlobalTaggingAPIv1() if err != nil { - return fmt.Errorf("Error getting global tagging client settings: %s", err) + tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_iam_access_tag", "create") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() } tagName := d.Get("name").(string) @@ -74,9 +78,10 @@ func resourceIBMIamAccessTagCreate(d *schema.ResourceData, meta interface{}) err TagType: &accessTagType, TagNames: add, } - results, _, err := gtClient.CreateTag(createTagOptions) + results, _, err := gtClient.CreateTagWithContext(context, createTagOptions) if err != nil { - return err + tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_iam_access_tag", "create") + return tfErr.GetDiag() } if results != nil { errMap := make([]globaltaggingv1.CreateTagResultsResultsItem, 0) @@ -86,9 +91,8 @@ func resourceIBMIamAccessTagCreate(d *schema.ResourceData, meta interface{}) err } } if len(errMap) > 0 { - output, err := json.MarshalIndent(errMap, "", " ") - log.Printf("err is %s", err) - return fmt.Errorf("[ERROR] Error while creating access tag(%s) : %s", tagName, string(output)) + output, _ := json.MarshalIndent(errMap, "", " ") + return diag.FromErr(fmt.Errorf("Error while creating access tag(%s) : %s", tagName, string(output))) } } @@ -98,19 +102,22 @@ func resourceIBMIamAccessTagCreate(d *schema.ResourceData, meta interface{}) err return nil } -func resourceIBMIamAccessTagRead(d *schema.ResourceData, meta interface{}) error { +func resourceIBMIamAccessTagRead(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { tagName := d.Id() gtClient, err := meta.(conns.ClientSession).GlobalTaggingAPIv1() if err != nil { - return fmt.Errorf("Error getting global tagging client settings: %s", err) + tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_iam_access_tag", "read") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() } accessTagType := "access" listTagsOptions := &globaltaggingv1.ListTagsOptions{ TagType: &accessTagType, } - taggingResult, _, err := gtClient.ListTags(listTagsOptions) + taggingResult, _, err := gtClient.ListTagsWithContext(context, listTagsOptions) if err != nil { - return err + tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_iam_access_tag", "read") + return tfErr.GetDiag() } var taglist []string @@ -127,11 +134,13 @@ func resourceIBMIamAccessTagRead(d *schema.ResourceData, meta interface{}) error return nil } -func resourceIBMIamAccessTagDelete(d *schema.ResourceData, meta interface{}) error { +func resourceIBMIamAccessTagDelete(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { gtClient, err := meta.(conns.ClientSession).GlobalTaggingAPIv1() if err != nil { - return fmt.Errorf("[ERROR] Error getting global tagging client settings: %s", err) + tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_iam_access_tag", "update") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() } tagName := d.Get("name").(string) accessTagType := "access" @@ -141,10 +150,10 @@ func resourceIBMIamAccessTagDelete(d *schema.ResourceData, meta interface{}) err TagType: &accessTagType, } - results, resp, err := gtClient.DeleteTag(deleteTagOptions) + results, resp, err := gtClient.DeleteTagWithContext(context, deleteTagOptions) if err != nil { - return fmt.Errorf("[ERROR] Error while deleting access tag(%s) : %v\n%v", tagName, err, resp) + return diag.FromErr(fmt.Errorf("Error while deleting access tag(%s) : %v\n%v", tagName, err, resp)) } if results != nil { errMap := make([]globaltaggingv1.DeleteTagResultsItem, 0) @@ -154,9 +163,8 @@ func resourceIBMIamAccessTagDelete(d *schema.ResourceData, meta interface{}) err } } if len(errMap) > 0 { - output, err := json.MarshalIndent(errMap, "", " ") - log.Printf("err is %s", err) - return fmt.Errorf("[ERROR] Error while deleting access tag(%s) : %s", tagName, string(output)) + output, _ := json.MarshalIndent(errMap, "", " ") + return diag.FromErr(fmt.Errorf("Error while deleting access tag(%s) : %s", tagName, string(output))) } } From 123a4f46e96767bfaa5b4d380b2f68220b6e3e7b Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Wed, 11 Sep 2024 13:26:56 +0200 Subject: [PATCH 08/15] Added new context functions. Now the code handles the error in the response on resource tags calls. --- .../resource_ibm_resource_tag.go | 114 +++++++++++++----- .../resource_ibm_resource_tag_test.go | 23 ++++ 2 files changed, 110 insertions(+), 27 deletions(-) diff --git a/ibm/service/globaltagging/resource_ibm_resource_tag.go b/ibm/service/globaltagging/resource_ibm_resource_tag.go index 34df86fc29..709910ad34 100644 --- a/ibm/service/globaltagging/resource_ibm_resource_tag.go +++ b/ibm/service/globaltagging/resource_ibm_resource_tag.go @@ -5,6 +5,7 @@ package globaltagging import ( "context" + "encoding/json" "fmt" "log" "os" @@ -18,6 +19,7 @@ import ( "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/validate" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" ) const ( @@ -32,11 +34,11 @@ const ( func ResourceIBMResourceTag() *schema.Resource { return &schema.Resource{ - Create: resourceIBMResourceTagCreate, - Read: resourceIBMResourceTagRead, - Update: resourceIBMResourceTagUpdate, - Delete: resourceIBMResourceTagDelete, - Importer: &schema.ResourceImporter{}, + CreateContext: resourceIBMResourceTagCreate, + ReadContext: resourceIBMResourceTagRead, + UpdateContext: resourceIBMResourceTagUpdate, + DeleteContext: resourceIBMResourceTagDelete, + Importer: &schema.ResourceImporter{}, CustomizeDiff: customdiff.Sequence( func(_ context.Context, diff *schema.ResourceDiff, v interface{}) error { @@ -125,19 +127,23 @@ func ResourceIBMResourceTagValidator() *validate.ResourceValidator { return &ibmResourceTagValidator } -func resourceIBMResourceTagCreate(d *schema.ResourceData, meta interface{}) error { +func resourceIBMResourceTagCreate(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var rType, tType string resources := []globaltaggingv1.Resource{} userDetails, err := meta.(conns.ClientSession).BluemixUserDetails() if err != nil { - return err + tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_resource_tag", "create") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() } accountID := userDetails.UserAccount gtClient, err := meta.(conns.ClientSession).GlobalTaggingAPIv1() if err != nil { - return fmt.Errorf("[ERROR] Error getting global tagging client settings: %s", err) + tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_resource_tag", "create") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() } resourceID := d.Get(resourceID).(string) @@ -187,9 +193,24 @@ func resourceIBMResourceTagCreate(d *schema.ResourceData, meta interface{}) erro } if len(add) > 0 { - _, resp, err := gtClient.AttachTag(AttachTagOptions) + results, fullResponse, err := gtClient.AttachTagWithContext(context, AttachTagOptions) if err != nil { - return fmt.Errorf("[ERROR] Error attaching resource tags : %v\n%s", resp, err) + tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_resource_tag", "create") + return tfErr.GetDiag() + } + + // Check if there are errors on the attach internal response + if results != nil { + errMap := make([]globaltaggingv1.TagResultsItem, 0) + for _, res := range results.Results { + if res.IsError != nil && *res.IsError { + errMap = append(errMap, res) + } + } + if len(errMap) > 0 { + output, _ := json.MarshalIndent(errMap, "", " ") + return diag.FromErr(fmt.Errorf("Error while creating tag: %s - Full response: %s", string(output), fullResponse)) + } } response, errored := flex.WaitForTagsAvailable(meta, resourceID, resourceType, tagType, news, d.Timeout(schema.TimeoutCreate)) if errored != nil { @@ -204,15 +225,17 @@ func resourceIBMResourceTagCreate(d *schema.ResourceData, meta interface{}) erro d.SetId(fmt.Sprintf("%s/%s", resourceID, rType)) } - return resourceIBMResourceTagRead(d, meta) + return resourceIBMResourceTagRead(context, d, meta) } -func resourceIBMResourceTagRead(d *schema.ResourceData, meta interface{}) error { +func resourceIBMResourceTagRead(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var rID, rType, tType string userDetails, err := meta.(conns.ClientSession).BluemixUserDetails() if err != nil { - return err + tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_resource_tag", "read") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() } acctID := userDetails.UserAccount @@ -221,10 +244,12 @@ func resourceIBMResourceTagRead(d *schema.ResourceData, meta interface{}) error } else { parts, err := flex.VmIdParts(d.Id()) if err != nil { - return err + tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_resource_tag", "read") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() } if len(parts) < 2 { - return fmt.Errorf("[ERROR] Incorrect ID %s: Id should be a combination of resourceID/resourceType", d.Id()) + return diag.FromErr(fmt.Errorf("Incorrect ID %s: Id should be a combination of resourceID/resourceType", d.Id())) } rID = parts[0] rType = parts[1] @@ -240,7 +265,7 @@ func resourceIBMResourceTagRead(d *schema.ResourceData, meta interface{}) error tagList, err := flex.GetGlobalTagsUsingSearchAPI(meta, rID, rType, tType) if err != nil { - return fmt.Errorf("[ERROR] Error getting resource tags for: %s with error : %s", rID, err) + return diag.FromErr(fmt.Errorf("Error getting resource tags for: %s with error : %s", rID, err)) } d.Set(resourceID, rID) @@ -250,7 +275,7 @@ func resourceIBMResourceTagRead(d *schema.ResourceData, meta interface{}) error return nil } -func resourceIBMResourceTagUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceIBMResourceTagUpdate(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var rID, rType, tType string if strings.HasPrefix(d.Id(), "crn:") { @@ -258,7 +283,9 @@ func resourceIBMResourceTagUpdate(d *schema.ResourceData, meta interface{}) erro } else { parts, err := flex.VmIdParts(d.Id()) if err != nil { - return err + tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_resource_tag", "update") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() } rID = parts[0] rType = parts[1] @@ -272,14 +299,14 @@ func resourceIBMResourceTagUpdate(d *schema.ResourceData, meta interface{}) erro oldList, newList := d.GetChange(tags) err := flex.UpdateGlobalTagsUsingCRN(oldList, newList, meta, rID, rType, tType) if err != nil { - return fmt.Errorf("[ERROR] Error on create of resource tags: %s", err) + return diag.FromErr(fmt.Errorf("Error on create of resource tags: %s", err)) } } - return resourceIBMResourceTagRead(d, meta) + return resourceIBMResourceTagRead(context, d, meta) } -func resourceIBMResourceTagDelete(d *schema.ResourceData, meta interface{}) error { +func resourceIBMResourceTagDelete(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var rID, rType string if strings.HasPrefix(d.Id(), "crn:") { @@ -287,7 +314,9 @@ func resourceIBMResourceTagDelete(d *schema.ResourceData, meta interface{}) erro } else { parts, err := flex.VmIdParts(d.Id()) if err != nil { - return err + tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_resource_tag", "delete") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() } rID = parts[0] rType = parts[1] @@ -295,7 +324,9 @@ func resourceIBMResourceTagDelete(d *schema.ResourceData, meta interface{}) erro gtClient, err := meta.(conns.ClientSession).GlobalTaggingAPIv1() if err != nil { - return fmt.Errorf("[ERROR] Error getting global tagging client settings: %s", err) + tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_resource_tag", "delete") + log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + return tfErr.GetDiag() } var remove []string @@ -322,17 +353,46 @@ func resourceIBMResourceTagDelete(d *schema.ResourceData, meta interface{}) erro TagType: &tType, } - _, resp, err := gtClient.DetachTag(detachTagOptions) + results, fullResponse, err := gtClient.DetachTagWithContext(context, detachTagOptions) if err != nil { - return fmt.Errorf("[ERROR] Error detaching resource tags %v: %s\n%s", remove, err, resp) + tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_resource_tag", "delete") + return tfErr.GetDiag() } + + // Check if there are errors on the detach internal response + if results != nil { + errMap := make([]globaltaggingv1.TagResultsItem, 0) + for _, res := range results.Results { + if res.IsError != nil && *res.IsError { + errMap = append(errMap, res) + } + } + if len(errMap) > 0 { + output, _ := json.MarshalIndent(errMap, "", " ") + return diag.FromErr(fmt.Errorf("Error while detaching tag: %s - Full response: %s", string(output), fullResponse)) + } + } + for _, v := range remove { delTagOptions := &globaltaggingv1.DeleteTagOptions{ TagName: flex.PtrToString(v), } - _, resp, err := gtClient.DeleteTag(delTagOptions) + results, fullResponse, err := gtClient.DeleteTagWithContext(context, delTagOptions) if err != nil { - return fmt.Errorf("[ERROR] Error deleting resource tag %v: %s\n%s", v, err, resp) + return diag.FromErr(fmt.Errorf("Error deleting resource tag %v: %s\n%s", v, err, fullResponse)) + } + + if results != nil { + errMap := make([]globaltaggingv1.DeleteTagResultsItem, 0) + for _, res := range results.Results { + if res.IsError != nil && *res.IsError { + errMap = append(errMap, res) + } + } + if len(errMap) > 0 { + output, _ := json.MarshalIndent(errMap, "", " ") + return diag.FromErr(fmt.Errorf("Error while deleting tag: %s - Full response: %s", string(output), fullResponse)) + } } } } diff --git a/ibm/service/globaltagging/resource_ibm_resource_tag_test.go b/ibm/service/globaltagging/resource_ibm_resource_tag_test.go index b7dca8d395..3867dc422e 100644 --- a/ibm/service/globaltagging/resource_ibm_resource_tag_test.go +++ b/ibm/service/globaltagging/resource_ibm_resource_tag_test.go @@ -41,6 +41,20 @@ func TestAccResourceTag_Basic(t *testing.T) { }, }) } + +func TestAccResourceTag_AccountOutOfScopeError(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckResourceTagCreateAccountOutOfScope(), + ExpectError: regexp.MustCompile("\"is_error\": true"), + }, + }, + }) +} + func TestAccResourceTag_Wait(t *testing.T) { name := fmt.Sprintf("tf-cos-%d", acctest.RandIntRange(10, 100)) @@ -122,6 +136,15 @@ func testAccCheckResourceTagCreate(name string) string { `, name) } +func testAccCheckResourceTagCreateAccountOutOfScope() string { + return fmt.Sprintf(` + resource "ibm_resource_tag" "tag" { + resource_id = "crn:v1:staging:public:cloud-object-storage:global:a/d99e99999dfe99ee999999f99bddd099:ab25d9be-5e0c-44dd-ad89-0bced3992758::" + tags = ["env:dev", "cpu:4"] + } +`) +} + func TestAccResourceTag_replace_Basic(t *testing.T) { name := fmt.Sprintf("tf-cos-%d", acctest.RandIntRange(10, 100)) From 44c4f4baa99a448d8acca1d561039d97ccad7816 Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Thu, 12 Sep 2024 06:47:16 +0200 Subject: [PATCH 09/15] Fixed 5635 --- ibm/flex/structures.go | 94 +++++++++++++++++-- .../resource_ibm_resource_tag.go | 63 +------------ .../resource_ibm_resource_tag_test.go | 6 +- 3 files changed, 90 insertions(+), 73 deletions(-) diff --git a/ibm/flex/structures.go b/ibm/flex/structures.go index da354497e6..79909fc004 100644 --- a/ibm/flex/structures.go +++ b/ibm/flex/structures.go @@ -2532,17 +2532,42 @@ func UpdateGlobalTagsUsingCRN(oldList, newList interface{}, meta interface{}, re } } - _, resp, err := gtClient.DetachTag(detachTagOptions) + results, fullResponse, err := gtClient.DetachTag(detachTagOptions) if err != nil { - return fmt.Errorf("[ERROR] Error detaching database tags %v: %s\n%s", remove, err, resp) + return fmt.Errorf("[ERROR] Error detaching database tags %v: %s\n%s", remove, err, fullResponse) + } + if results != nil { + errMap := make([]globaltaggingv1.TagResultsItem, 0) + for _, res := range results.Results { + if res.IsError != nil && *res.IsError { + errMap = append(errMap, res) + } + } + if len(errMap) > 0 { + output, _ := json.MarshalIndent(errMap, "", " ") + return fmt.Errorf("[ERROR] Error detaching tag %v: %s\n%s", remove, string(output), fullResponse) + } } for _, v := range remove { delTagOptions := &globaltaggingv1.DeleteTagOptions{ TagName: PtrToString(v), } - _, resp, err := gtClient.DeleteTag(delTagOptions) + results, fullResponse, err := gtClient.DeleteTag(delTagOptions) if err != nil { - return fmt.Errorf("[ERROR] Error deleting database tag %v: %s\n%s", v, err, resp) + return fmt.Errorf("[ERROR] Error deleting database tag %v: %s\n%s", v, err, fullResponse) + } + + if results != nil { + errMap := make([]globaltaggingv1.DeleteTagResultsItem, 0) + for _, res := range results.Results { + if res.IsError != nil && *res.IsError { + errMap = append(errMap, res) + } + } + if len(errMap) > 0 { + output, _ := json.MarshalIndent(errMap, "", " ") + return fmt.Errorf("[ERROR] Error deleting tag %s: %s\n%s", PtrToString(v), string(output), fullResponse) + } } } } @@ -2624,7 +2649,7 @@ func GetTagsUsingCRN(meta interface{}, resourceCRN string) (*schema.Set, error) } func UpdateTagsUsingCRN(oldList, newList interface{}, meta interface{}, resourceCRN string) error { - gtClient, err := meta.(conns.ClientSession).GlobalTaggingAPI() + gtClient, err := meta.(conns.ClientSession).GlobalTaggingAPIv1() if err != nil { return fmt.Errorf("[ERROR] Error getting global tagging client settings: %s", err) } @@ -2654,24 +2679,75 @@ func UpdateTagsUsingCRN(oldList, newList interface{}, meta interface{}, resource add = append(add, envTags...) } + resources := []globaltaggingv1.Resource{} + r := globaltaggingv1.Resource{ResourceID: &resourceCRN} + resources = append(resources, r) + if len(remove) > 0 { - _, err := gtClient.Tags().DetachTags(resourceCRN, remove) + detachTagOptions := &globaltaggingv1.DetachTagOptions{} + detachTagOptions.Resources = resources + detachTagOptions.TagNames = remove + + results, fullResponse, err := gtClient.DetachTag(detachTagOptions) if err != nil { return fmt.Errorf("[ERROR] Error detaching database tags %v: %s", remove, err) } + if results != nil { + errMap := make([]globaltaggingv1.TagResultsItem, 0) + for _, res := range results.Results { + if res.IsError != nil && *res.IsError { + errMap = append(errMap, res) + } + } + if len(errMap) > 0 { + output, _ := json.MarshalIndent(errMap, "", " ") + return fmt.Errorf("[ERROR] Error detaching tag %v: %s\n%s", remove, string(output), fullResponse) + } + } for _, v := range remove { - _, err := gtClient.Tags().DeleteTag(v) + delTagOptions := &globaltaggingv1.DeleteTagOptions{ + TagName: PtrToString(v), + } + results, fullResponse, err := gtClient.DeleteTag(delTagOptions) if err != nil { - return fmt.Errorf("[ERROR] Error deleting database tag %v: %s", v, err) + return fmt.Errorf("[ERROR] Error deleting database tag %v: %s\n%s", v, err, fullResponse) + } + + if results != nil { + errMap := make([]globaltaggingv1.DeleteTagResultsItem, 0) + for _, res := range results.Results { + if res.IsError != nil && *res.IsError { + errMap = append(errMap, res) + } + } + if len(errMap) > 0 { + output, _ := json.MarshalIndent(errMap, "", " ") + return fmt.Errorf("[ERROR] Error deleting tag %s: %s\n%s", PtrToString(v), string(output), fullResponse) + } } } } if len(add) > 0 { - _, err := gtClient.Tags().AttachTags(resourceCRN, add) + AttachTagOptions := &globaltaggingv1.AttachTagOptions{} + AttachTagOptions.Resources = resources + AttachTagOptions.TagNames = add + results, fullResponse, err := gtClient.AttachTag(AttachTagOptions) if err != nil { return fmt.Errorf("[ERROR] Error updating database tags %v : %s", add, err) } + if results != nil { + errMap := make([]globaltaggingv1.TagResultsItem, 0) + for _, res := range results.Results { + if res.IsError != nil && *res.IsError { + errMap = append(errMap, res) + } + } + if len(errMap) > 0 { + output, _ := json.MarshalIndent(errMap, "", " ") + return fmt.Errorf("Error while updating tag: %s - Full response: %s", string(output), fullResponse) + } + } } return nil diff --git a/ibm/service/globaltagging/resource_ibm_resource_tag.go b/ibm/service/globaltagging/resource_ibm_resource_tag.go index 709910ad34..58b89f1534 100644 --- a/ibm/service/globaltagging/resource_ibm_resource_tag.go +++ b/ibm/service/globaltagging/resource_ibm_resource_tag.go @@ -322,19 +322,8 @@ func resourceIBMResourceTagDelete(context context.Context, d *schema.ResourceDat rType = parts[1] } - gtClient, err := meta.(conns.ClientSession).GlobalTaggingAPIv1() - if err != nil { - tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_resource_tag", "delete") - log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) - return tfErr.GetDiag() - } - var remove []string removeTags := d.Get(tags).(*schema.Set) - remove = make([]string, len(removeTags.List())) - for i, v := range removeTags.List() { - remove[i] = fmt.Sprint(v) - } var tType string if v, ok := d.GetOk(tagType); ok && v != nil { tType = v.(string) @@ -343,57 +332,9 @@ func resourceIBMResourceTagDelete(context context.Context, d *schema.ResourceDat } if len(remove) > 0 { - resources := []globaltaggingv1.Resource{} - r := globaltaggingv1.Resource{ResourceID: flex.PtrToString(rID), ResourceType: flex.PtrToString(rType)} - resources = append(resources, r) - - detachTagOptions := &globaltaggingv1.DetachTagOptions{ - Resources: resources, - TagNames: remove, - TagType: &tType, - } - - results, fullResponse, err := gtClient.DetachTagWithContext(context, detachTagOptions) + err := flex.UpdateGlobalTagsUsingCRN(removeTags, nil, meta, rID, rType, tType) if err != nil { - tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_resource_tag", "delete") - return tfErr.GetDiag() - } - - // Check if there are errors on the detach internal response - if results != nil { - errMap := make([]globaltaggingv1.TagResultsItem, 0) - for _, res := range results.Results { - if res.IsError != nil && *res.IsError { - errMap = append(errMap, res) - } - } - if len(errMap) > 0 { - output, _ := json.MarshalIndent(errMap, "", " ") - return diag.FromErr(fmt.Errorf("Error while detaching tag: %s - Full response: %s", string(output), fullResponse)) - } - } - - for _, v := range remove { - delTagOptions := &globaltaggingv1.DeleteTagOptions{ - TagName: flex.PtrToString(v), - } - results, fullResponse, err := gtClient.DeleteTagWithContext(context, delTagOptions) - if err != nil { - return diag.FromErr(fmt.Errorf("Error deleting resource tag %v: %s\n%s", v, err, fullResponse)) - } - - if results != nil { - errMap := make([]globaltaggingv1.DeleteTagResultsItem, 0) - for _, res := range results.Results { - if res.IsError != nil && *res.IsError { - errMap = append(errMap, res) - } - } - if len(errMap) > 0 { - output, _ := json.MarshalIndent(errMap, "", " ") - return diag.FromErr(fmt.Errorf("Error while deleting tag: %s - Full response: %s", string(output), fullResponse)) - } - } + return diag.FromErr(fmt.Errorf("Error on deleting tags: %s", err)) } } return nil diff --git a/ibm/service/globaltagging/resource_ibm_resource_tag_test.go b/ibm/service/globaltagging/resource_ibm_resource_tag_test.go index 3867dc422e..3451162b47 100644 --- a/ibm/service/globaltagging/resource_ibm_resource_tag_test.go +++ b/ibm/service/globaltagging/resource_ibm_resource_tag_test.go @@ -42,13 +42,13 @@ func TestAccResourceTag_Basic(t *testing.T) { }) } -func TestAccResourceTag_AccountOutOfScopeError(t *testing.T) { +func TestAccResourceTag_FakeCrnExpectingError(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { acc.TestAccPreCheck(t) }, Providers: acc.TestAccProviders, Steps: []resource.TestStep{ { - Config: testAccCheckResourceTagCreateAccountOutOfScope(), + Config: testAccCheckResourceTagCreateFakeCrnExpectingError(), ExpectError: regexp.MustCompile("\"is_error\": true"), }, }, @@ -136,7 +136,7 @@ func testAccCheckResourceTagCreate(name string) string { `, name) } -func testAccCheckResourceTagCreateAccountOutOfScope() string { +func testAccCheckResourceTagCreateFakeCrnExpectingError() string { return fmt.Sprintf(` resource "ibm_resource_tag" "tag" { resource_id = "crn:v1:staging:public:cloud-object-storage:global:a/d99e99999dfe99ee999999f99bddd099:ab25d9be-5e0c-44dd-ad89-0bced3992758::" From d55eec9982e13d222ad6d925b950d3dfab67e8d3 Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Thu, 12 Sep 2024 09:57:39 +0200 Subject: [PATCH 10/15] Fix --- .../globaltagging/resource_ibm_resource_tag.go | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/ibm/service/globaltagging/resource_ibm_resource_tag.go b/ibm/service/globaltagging/resource_ibm_resource_tag.go index d34a7ae5f3..58b89f1534 100644 --- a/ibm/service/globaltagging/resource_ibm_resource_tag.go +++ b/ibm/service/globaltagging/resource_ibm_resource_tag.go @@ -330,26 +330,9 @@ func resourceIBMResourceTagDelete(context context.Context, d *schema.ResourceDat } else { tType = "user" } - var tType string - if v, ok := d.GetOk(tagType); ok && v != nil { - tType = v.(string) - } else { - tType = "user" - } if len(remove) > 0 { err := flex.UpdateGlobalTagsUsingCRN(removeTags, nil, meta, rID, rType, tType) - resources := []globaltaggingv1.Resource{} - r := globaltaggingv1.Resource{ResourceID: flex.PtrToString(rID), ResourceType: flex.PtrToString(rType)} - resources = append(resources, r) - - detachTagOptions := &globaltaggingv1.DetachTagOptions{ - Resources: resources, - TagNames: remove, - TagType: &tType, - } - - _, resp, err := gtClient.DetachTag(detachTagOptions) if err != nil { return diag.FromErr(fmt.Errorf("Error on deleting tags: %s", err)) } From f467724e76768286571e56b139b6c694cf338dae Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Fri, 13 Sep 2024 12:38:13 +0200 Subject: [PATCH 11/15] Fixed --- ibm/flex/structures.go | 57 +++---- .../resource_ibm_iam_access_tag.go | 4 +- .../resource_ibm_iam_access_tag_test.go | 24 +-- .../resource_ibm_resource_tag.go | 8 +- .../resource_ibm_resource_tag_test.go | 140 ++++++++++++++---- 5 files changed, 148 insertions(+), 85 deletions(-) diff --git a/ibm/flex/structures.go b/ibm/flex/structures.go index 79909fc004..8a705f475c 100644 --- a/ibm/flex/structures.go +++ b/ibm/flex/structures.go @@ -2401,34 +2401,46 @@ func GetTags(d *schema.ResourceData, meta interface{}) error { // } func GetGlobalTagsUsingCRN(meta interface{}, resourceID, resourceType, tagType string) (*schema.Set, error) { + taggingResult, err := GetGlobalTagsUsingSearchAPI(meta, resourceID, resourceType, tagType) + if err != nil { + return nil, err + } + return taggingResult, nil +} + +func GetTagsUsingResourceCRNFromTaggingApi(meta interface{}, resourceID, resourceType, tagType string) (*schema.Set, error) { + gtClient, err := meta.(conns.ClientSession).GlobalTaggingAPIv1() + if err != nil { + return nil, fmt.Errorf("[ERROR] Error getting global tagging client settings: %s", err) + } userDetails, err := meta.(conns.ClientSession).BluemixUserDetails() if err != nil { return nil, err } accountID := userDetails.UserAccount ListTagsOptions := &globaltaggingv1.ListTagsOptions{} - if resourceID != "" { - ListTagsOptions.AttachedTo = &resourceID - } + ListTagsOptions.AttachedTo = &resourceID if strings.HasPrefix(resourceType, "Softlayer_") { ListTagsOptions.Providers = []string{"ims"} } if len(tagType) > 0 { ListTagsOptions.TagType = PtrToString(tagType) - if tagType == "service" { ListTagsOptions.AccountID = PtrToString(accountID) } } - taggingResult, err := GetGlobalTagsUsingSearchAPI(meta, resourceID, resourceType, tagType) + taggingResult, _, err := gtClient.ListTags(ListTagsOptions) if err != nil { return nil, err } - return taggingResult, nil + var taglist []string + for _, item := range taggingResult.Items { + taglist = append(taglist, *item.Name) + } + return NewStringSet(ResourceIBMVPCHash, taglist), nil } func GetGlobalTagsUsingSearchAPI(meta interface{}, resourceID, resourceType, tagType string) (*schema.Set, error) { - gsClient, err := meta.(conns.ClientSession).GlobalSearchAPIV2() if err != nil { return nil, fmt.Errorf("[ERROR] Error getting global search client settings: %s", err) @@ -2531,10 +2543,9 @@ func UpdateGlobalTagsUsingCRN(oldList, newList interface{}, meta interface{}, re detachTagOptions.AccountID = PtrToString(acctID) } } - results, fullResponse, err := gtClient.DetachTag(detachTagOptions) if err != nil { - return fmt.Errorf("[ERROR] Error detaching database tags %v: %s\n%s", remove, err, fullResponse) + return fmt.Errorf("[ERROR] Error detaching tags calling api %v: %s\n%s", remove, err, fullResponse) } if results != nil { errMap := make([]globaltaggingv1.TagResultsItem, 0) @@ -2545,29 +2556,7 @@ func UpdateGlobalTagsUsingCRN(oldList, newList interface{}, meta interface{}, re } if len(errMap) > 0 { output, _ := json.MarshalIndent(errMap, "", " ") - return fmt.Errorf("[ERROR] Error detaching tag %v: %s\n%s", remove, string(output), fullResponse) - } - } - for _, v := range remove { - delTagOptions := &globaltaggingv1.DeleteTagOptions{ - TagName: PtrToString(v), - } - results, fullResponse, err := gtClient.DeleteTag(delTagOptions) - if err != nil { - return fmt.Errorf("[ERROR] Error deleting database tag %v: %s\n%s", v, err, fullResponse) - } - - if results != nil { - errMap := make([]globaltaggingv1.DeleteTagResultsItem, 0) - for _, res := range results.Results { - if res.IsError != nil && *res.IsError { - errMap = append(errMap, res) - } - } - if len(errMap) > 0 { - output, _ := json.MarshalIndent(errMap, "", " ") - return fmt.Errorf("[ERROR] Error deleting tag %s: %s\n%s", PtrToString(v), string(output), fullResponse) - } + return fmt.Errorf("[ERROR] Error detaching tag in results %v: %s\n%s", remove, string(output), fullResponse) } } } @@ -2649,6 +2638,8 @@ func GetTagsUsingCRN(meta interface{}, resourceCRN string) (*schema.Set, error) } func UpdateTagsUsingCRN(oldList, newList interface{}, meta interface{}, resourceCRN string) error { + log.Println("UpdateTagsUsingCRN start") + fmt.Println("[INFO] UpdateTagsUsingCRN start") gtClient, err := meta.(conns.ClientSession).GlobalTaggingAPIv1() if err != nil { return fmt.Errorf("[ERROR] Error getting global tagging client settings: %s", err) @@ -2722,7 +2713,7 @@ func UpdateTagsUsingCRN(oldList, newList interface{}, meta interface{}, resource } if len(errMap) > 0 { output, _ := json.MarshalIndent(errMap, "", " ") - return fmt.Errorf("[ERROR] Error deleting tag %s: %s\n%s", PtrToString(v), string(output), fullResponse) + return fmt.Errorf("[ERROR] Error deleting tag %s: %s\n%s", v, string(output), fullResponse) } } } diff --git a/ibm/service/globaltagging/resource_ibm_iam_access_tag.go b/ibm/service/globaltagging/resource_ibm_iam_access_tag.go index fa7874bef7..a1844c8fc3 100644 --- a/ibm/service/globaltagging/resource_ibm_iam_access_tag.go +++ b/ibm/service/globaltagging/resource_ibm_iam_access_tag.go @@ -153,7 +153,7 @@ func resourceIBMIamAccessTagDelete(context context.Context, d *schema.ResourceDa results, resp, err := gtClient.DeleteTagWithContext(context, deleteTagOptions) if err != nil { - return diag.FromErr(fmt.Errorf("Error while deleting access tag(%s) : %v\n%v", tagName, err, resp)) + return diag.FromErr(fmt.Errorf("Error while deleting access tag calling api (%s) : %v\n%v", tagName, err, resp)) } if results != nil { errMap := make([]globaltaggingv1.DeleteTagResultsItem, 0) @@ -164,7 +164,7 @@ func resourceIBMIamAccessTagDelete(context context.Context, d *schema.ResourceDa } if len(errMap) > 0 { output, _ := json.MarshalIndent(errMap, "", " ") - return diag.FromErr(fmt.Errorf("Error while deleting access tag(%s) : %s", tagName, string(output))) + return diag.FromErr(fmt.Errorf("Error while deleting access tag in results (%s) : %s", tagName, string(output))) } } diff --git a/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go b/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go index a6d973def4..e4a4f2362c 100644 --- a/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go +++ b/ibm/service/globaltagging/resource_ibm_iam_access_tag_test.go @@ -6,7 +6,6 @@ package globaltagging_test import ( "fmt" "regexp" - "strings" "testing" acc "github.com/IBM-Cloud/terraform-provider-ibm/ibm/acctest" @@ -44,10 +43,6 @@ func TestAccIamAccessTag_Basic(t *testing.T) { } func TestAccIamAccessTag_Usage(t *testing.T) { name := fmt.Sprintf("tf%d:iam-access%d", acctest.RandIntRange(10, 100), acctest.RandIntRange(10, 100)) - publicKey := strings.TrimSpace(` -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR -`) - sshkeyname := fmt.Sprintf("tfssh-createname-%d", acctest.RandIntRange(10, 100)) resource.Test(t, resource.TestCase{ PreCheck: func() { acc.TestAccPreCheck(t) }, Providers: acc.TestAccProviders, @@ -63,7 +58,7 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVE ), }, resource.TestStep{ - Config: testAccCheckIamAccessTagUsage(name, sshkeyname, publicKey), + Config: testAccCheckIamAccessTagUsage(name), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckIamAccessTagExists("ibm_iam_access_tag.tag"), resource.TestCheckResourceAttr("ibm_iam_access_tag.tag", "id", name), @@ -129,19 +124,24 @@ func testAccCheckIamAccessTagCreate(name string) string { } `, name) } -func testAccCheckIamAccessTagUsage(name, sshkeyname, publicKey string) string { +func testAccCheckIamAccessTagUsage(name string) string { + resource_group_name := fmt.Sprintf("tf%d-iam-access%d", acctest.RandIntRange(10, 100), acctest.RandIntRange(10, 100)) return fmt.Sprintf(` + data "ibm_resource_group" "group" { + name = "Default" + } resource "ibm_iam_access_tag" "tag" { name = "%s" } - resource "ibm_is_ssh_key" "key" { - name = "%s" - public_key = "%s" + resource "ibm_cd_toolchain" "cd_toolchain_instance" { + description = "Terraform test" + name = "%s-toolchain" + resource_group_id = data.ibm_resource_group.group.id } resource "ibm_resource_tag" "tag" { - resource_id = ibm_is_ssh_key.key.crn + resource_id = ibm_cd_toolchain.cd_toolchain_instance.crn tags = [ibm_iam_access_tag.tag.name] tag_type = "access" } -`, name, sshkeyname, publicKey) +`, name, resource_group_name) } diff --git a/ibm/service/globaltagging/resource_ibm_resource_tag.go b/ibm/service/globaltagging/resource_ibm_resource_tag.go index 58b89f1534..aa23bd1f73 100644 --- a/ibm/service/globaltagging/resource_ibm_resource_tag.go +++ b/ibm/service/globaltagging/resource_ibm_resource_tag.go @@ -308,21 +308,18 @@ func resourceIBMResourceTagUpdate(context context.Context, d *schema.ResourceDat func resourceIBMResourceTagDelete(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var rID, rType string - if strings.HasPrefix(d.Id(), "crn:") { rID = d.Id() } else { parts, err := flex.VmIdParts(d.Id()) if err != nil { tfErr := flex.TerraformErrorf(err, err.Error(), "ibm_resource_tag", "delete") - log.Printf("[DEBUG]\n%s", tfErr.GetDebugMessage()) + log.Printf("[ERROR] Error in deleting.\n%s", tfErr.GetDebugMessage()) return tfErr.GetDiag() } rID = parts[0] rType = parts[1] } - - var remove []string removeTags := d.Get(tags).(*schema.Set) var tType string if v, ok := d.GetOk(tagType); ok && v != nil { @@ -330,8 +327,7 @@ func resourceIBMResourceTagDelete(context context.Context, d *schema.ResourceDat } else { tType = "user" } - - if len(remove) > 0 { + if len(removeTags.List()) > 0 { err := flex.UpdateGlobalTagsUsingCRN(removeTags, nil, meta, rID, rType, tType) if err != nil { return diag.FromErr(fmt.Errorf("Error on deleting tags: %s", err)) diff --git a/ibm/service/globaltagging/resource_ibm_resource_tag_test.go b/ibm/service/globaltagging/resource_ibm_resource_tag_test.go index 3451162b47..414a1a2d4b 100644 --- a/ibm/service/globaltagging/resource_ibm_resource_tag_test.go +++ b/ibm/service/globaltagging/resource_ibm_resource_tag_test.go @@ -7,6 +7,7 @@ import ( "fmt" "log" "regexp" + "strings" "testing" acc "github.com/IBM-Cloud/terraform-provider-ibm/ibm/acctest" @@ -18,14 +19,17 @@ import ( func TestAccResourceTag_Basic(t *testing.T) { name := fmt.Sprintf("tf-cos-%d", acctest.RandIntRange(10, 100)) + var tags []string + tags = append(tags, "env:dev") + tags = append(tags, "cpu:4") resource.Test(t, resource.TestCase{ - PreCheck: func() { acc.TestAccPreCheck(t) }, - Providers: acc.TestAccProviders, + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckTagDestroy("ibm_resource_tag.tag", "user", tags), Steps: []resource.TestStep{ - { - Config: testAccCheckResourceTagCreate(name), + Config: testAccCheckResourceTagCreate(name, tags), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckResourceTagExists("ibm_resource_tag.tag"), resource.TestCheckResourceAttr("ibm_resource_tag.tag", "tags.#", "2"), @@ -43,38 +47,43 @@ func TestAccResourceTag_Basic(t *testing.T) { } func TestAccResourceTag_FakeCrnExpectingError(t *testing.T) { + var tags []string + tags = append(tags, "env:dev") + tags = append(tags, "cpu:4") resource.Test(t, resource.TestCase{ PreCheck: func() { acc.TestAccPreCheck(t) }, Providers: acc.TestAccProviders, Steps: []resource.TestStep{ { - Config: testAccCheckResourceTagCreateFakeCrnExpectingError(), + Config: testAccCheckResourceTagCreateFakeCrnExpectingError(tags), ExpectError: regexp.MustCompile("\"is_error\": true"), }, }, }) } -func TestAccResourceTag_Wait(t *testing.T) { - name := fmt.Sprintf("tf-cos-%d", acctest.RandIntRange(10, 100)) - +func TestAccResourceTag_AttachOnExistingResource(t *testing.T) { + crn := "crn:v1:bluemix:public:toolchain:eu-gb:a/970f5cb4bbc04119ab0a0f399e4b776c:8784b8c3-1c7f-476a-ac30-50ae07e3cce3::" + var tags []string + tags = append(tags, "env:dev") + tags = append(tags, "cpu:4") resource.Test(t, resource.TestCase{ - PreCheck: func() { acc.TestAccPreCheck(t) }, - Providers: acc.TestAccProviders, + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckTagOnExistingResourceDestroy(crn, "ibm_resource_tag.tag", "user", tags), Steps: []resource.TestStep{ - { - Config: testAccCheckResourceTagWaitCreate(name), + Config: testAccCheckResourceAttachOnExistingResource(crn, tags), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckResourceTagExists("ibm_resource_tag.tag"), - resource.TestCheckResourceAttr("ibm_resource_tag.tag", "tags.#", "3"), + resource.TestCheckResourceAttr("ibm_resource_tag.tag", "tags.#", "2"), ), }, }, }) } -func testAccCheckResourceTagExists(n string) resource.TestCheckFunc { +func testAccCheckTagDestroy(n, tagType string, tagNames []string) resource.TestCheckFunc { return func(s *terraform.State) error { var resourceID string rs, ok := s.RootModule().Resources[n] @@ -86,7 +95,80 @@ func testAccCheckResourceTagExists(n string) resource.TestCheckFunc { if err != nil { return err } + if crn.MatchString(rs.Primary.ID) { + resourceID = rs.Primary.ID + } else { + parts, err := flex.VmIdParts(rs.Primary.ID) + if err != nil { + return err + } + resourceID = parts[0] + } + results, errorGet := flex.GetTagsUsingResourceCRNFromTaggingApi(acc.TestAccProvider.Meta(), resourceID, "", tagType) + if errorGet != nil { + return fmt.Errorf("Error on get of resource tags (%s) : %s", resourceID, errorGet) + } + var taglist []string + for _, v := range results.List() { + taglist = append(taglist, fmt.Sprint(v)) + } + existingAccessTags := flex.NewStringSet(flex.ResourceIBMVPCHash, taglist) + for _, tagName := range tagNames { + if existingAccessTags.Contains(tagName) { + return fmt.Errorf("Tag still exists") + } + } + return nil + } +} +func testAccCheckTagOnExistingResourceDestroy(resourceCrn, n, tagType string, tagNames []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + var resourceID string + _, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + crnRegex := "^crn:v1(:[a-zA-Z0-9 \\-\\._~\\*\\+,;=!$&'\\(\\)\\/\\?#\\[\\]@]*){8}$|^[0-9]+$" + crn, err := regexp.Compile(crnRegex) + if err != nil { + return err + } + if crn.MatchString(resourceCrn) { + resourceID = resourceCrn + } else { + return fmt.Errorf("CRN not correct: %s", resourceCrn) + } + results, errorGet := flex.GetTagsUsingResourceCRNFromTaggingApi(acc.TestAccProvider.Meta(), resourceID, "", tagType) + if errorGet != nil { + return fmt.Errorf("Error on get of resource tags (%s) : %s", resourceID, errorGet) + } + var taglist []string + for _, v := range results.List() { + taglist = append(taglist, fmt.Sprint(v)) + } + existingAccessTags := flex.NewStringSet(flex.ResourceIBMVPCHash, taglist) + for _, tagName := range tagNames { + if existingAccessTags.Contains(tagName) { + return fmt.Errorf("Tag still exists") + } + } + return nil + } +} + +func testAccCheckResourceTagExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + var resourceID string + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + crnRegex := "^crn:v1(:[a-zA-Z0-9 \\-\\._~\\*\\+,;=!$&'\\(\\)\\/\\?#\\[\\]@]*){8}$|^[0-9]+$" + crn, err := regexp.Compile(crnRegex) + if err != nil { + return err + } if crn.MatchString(rs.Primary.ID) { resourceID = rs.Primary.ID } else { @@ -105,7 +187,7 @@ func testAccCheckResourceTagExists(n string) resource.TestCheckFunc { } } -func testAccCheckResourceTagWaitCreate(name string) string { +func testAccCheckResourceTagCreate(name string, tagNames []string) string { return fmt.Sprintf(` resource "ibm_resource_instance" "resource_1" { name = "%s" @@ -116,33 +198,27 @@ func testAccCheckResourceTagWaitCreate(name string) string { resource "ibm_resource_tag" "tag" { resource_id = ibm_resource_instance.resource_1.crn - tags = ["env:dev", "cpu:4", "user:8"] + tags = ["%s"] } -`, name) +`, name, strings.Join(tagNames[:], "\",\"")) } -func testAccCheckResourceTagCreate(name string) string { - return fmt.Sprintf(` - resource "ibm_resource_instance" "resource_1" { - name = "%s" - service = "cloud-object-storage" - plan = "lite" - location = "global" - } +func testAccCheckResourceTagCreateFakeCrnExpectingError(tagNames []string) string { + return fmt.Sprintf(` resource "ibm_resource_tag" "tag" { - resource_id = ibm_resource_instance.resource_1.crn - tags = ["env:dev", "cpu:4"] + resource_id = "crn:v1:staging:public:cloud-object-storage:global:a/d99e99999dfe99ee999999f99bddd099:ab99d9be-9e9c-99dd-ad99-9bced9999999::" + tags = ["%s"] } -`, name) +`, strings.Join(tagNames[:], "\",\"")) } -func testAccCheckResourceTagCreateFakeCrnExpectingError() string { +func testAccCheckResourceAttachOnExistingResource(crn string, tagNames []string) string { return fmt.Sprintf(` resource "ibm_resource_tag" "tag" { - resource_id = "crn:v1:staging:public:cloud-object-storage:global:a/d99e99999dfe99ee999999f99bddd099:ab25d9be-5e0c-44dd-ad89-0bced3992758::" - tags = ["env:dev", "cpu:4"] + resource_id = "%s" + tags = ["%s"] } -`) +`, crn, strings.Join(tagNames[:], "\",\"")) } func TestAccResourceTag_replace_Basic(t *testing.T) { From 9ec21a3ae511637f8cbfd71c561a0643a612cc04 Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Fri, 13 Sep 2024 12:42:55 +0200 Subject: [PATCH 12/15] Removed logs line --- ibm/flex/structures.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ibm/flex/structures.go b/ibm/flex/structures.go index 8a705f475c..147a0d6bba 100644 --- a/ibm/flex/structures.go +++ b/ibm/flex/structures.go @@ -2638,8 +2638,6 @@ func GetTagsUsingCRN(meta interface{}, resourceCRN string) (*schema.Set, error) } func UpdateTagsUsingCRN(oldList, newList interface{}, meta interface{}, resourceCRN string) error { - log.Println("UpdateTagsUsingCRN start") - fmt.Println("[INFO] UpdateTagsUsingCRN start") gtClient, err := meta.(conns.ClientSession).GlobalTaggingAPIv1() if err != nil { return fmt.Errorf("[ERROR] Error getting global tagging client settings: %s", err) @@ -2681,7 +2679,7 @@ func UpdateTagsUsingCRN(oldList, newList interface{}, meta interface{}, resource results, fullResponse, err := gtClient.DetachTag(detachTagOptions) if err != nil { - return fmt.Errorf("[ERROR] Error detaching database tags %v: %s", remove, err) + return fmt.Errorf("[ERROR] Error detaching tags %v: %s", remove, err) } if results != nil { errMap := make([]globaltaggingv1.TagResultsItem, 0) @@ -2701,7 +2699,7 @@ func UpdateTagsUsingCRN(oldList, newList interface{}, meta interface{}, resource } results, fullResponse, err := gtClient.DeleteTag(delTagOptions) if err != nil { - return fmt.Errorf("[ERROR] Error deleting database tag %v: %s\n%s", v, err, fullResponse) + return fmt.Errorf("[ERROR] Error deleting tag %v: %s\n%s", v, err, fullResponse) } if results != nil { @@ -2725,7 +2723,7 @@ func UpdateTagsUsingCRN(oldList, newList interface{}, meta interface{}, resource AttachTagOptions.TagNames = add results, fullResponse, err := gtClient.AttachTag(AttachTagOptions) if err != nil { - return fmt.Errorf("[ERROR] Error updating database tags %v : %s", add, err) + return fmt.Errorf("[ERROR] Error updating tags %v : %s", add, err) } if results != nil { errMap := make([]globaltaggingv1.TagResultsItem, 0) From 3f71c176c0f2441ca2f2a5536852596be2b677d8 Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Fri, 4 Oct 2024 09:15:42 +0200 Subject: [PATCH 13/15] Updated doc --- website/docs/r/resource_tag.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/resource_tag.html.markdown b/website/docs/r/resource_tag.html.markdown index 84cc6f229a..3c5b26b505 100644 --- a/website/docs/r/resource_tag.html.markdown +++ b/website/docs/r/resource_tag.html.markdown @@ -44,7 +44,7 @@ The `ibm_resource_tag` resource provides the following [Timeouts](https://www.te Review the argument references that you can specify for your resource. - `resource_id` - (Required, String) The CRN of the resource on which the tags is be attached. -- `resource_type` - (Optional, String) The resource type on which the tags should be attached. +- `resource_type` - (Optional, String) The resource type on which the tags should be attached. This is valid for SoftLayer (IMS) resources only. The resource_type allowed values are: `SoftLayer_Virtual_DedicatedHost`, `SoftLayer_Hardware`, `SoftLayer_Hardware_Server`, `SoftLayer_Network_Application_Delivery_Controller`, `SoftLayer_Network_Vlan`, `SoftLayer_Network_Vlan_Firewall`, `SoftLayer_Network_Component_Firewall`, `SoftLayer_Network_Firewall_Module_Context`, `SoftLayer_Virtual_Guest`. - `tag_type` - (Optional, String) Type of the tag. Supported values are: `user` or `access`. The default value is user. - `tags` - (Required, Array of strings) List of tags associated with resource instance. - `replace` - (Optional, Bool) If true, it indicates that the attaching operation is a replacement operation From 3e8638dc785293fccbb9bdacc99d950d4aceac4f Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Fri, 4 Oct 2024 09:19:22 +0200 Subject: [PATCH 14/15] Fix --- website/docs/r/resource_tag.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/resource_tag.html.markdown b/website/docs/r/resource_tag.html.markdown index 3c5b26b505..0631c6905c 100644 --- a/website/docs/r/resource_tag.html.markdown +++ b/website/docs/r/resource_tag.html.markdown @@ -44,7 +44,7 @@ The `ibm_resource_tag` resource provides the following [Timeouts](https://www.te Review the argument references that you can specify for your resource. - `resource_id` - (Required, String) The CRN of the resource on which the tags is be attached. -- `resource_type` - (Optional, String) The resource type on which the tags should be attached. This is valid for SoftLayer (IMS) resources only. The resource_type allowed values are: `SoftLayer_Virtual_DedicatedHost`, `SoftLayer_Hardware`, `SoftLayer_Hardware_Server`, `SoftLayer_Network_Application_Delivery_Controller`, `SoftLayer_Network_Vlan`, `SoftLayer_Network_Vlan_Firewall`, `SoftLayer_Network_Component_Firewall`, `SoftLayer_Network_Firewall_Module_Context`, `SoftLayer_Virtual_Guest`. +- `resource_type` - (Optional, String) The resource type on which the tags should be attached. This is valid for SoftLayer (IMS) resources only. The resource_type allowed values are: `SoftLayer_Virtual_DedicatedHost`, `SoftLayer_Hardware`, `SoftLayer_Hardware_Server`, `SoftLayer_Network_Application_Delivery_Controller`, `SoftLayer_Network_Vlan`, `SoftLayer_Network_Vlan_Firewall`, `SoftLayer_Network_Component_Firewall`, `SoftLayer_Network_Firewall_Module_Context`, `SoftLayer_Virtual_Guest`. A wrong value would result in an error in the `terraform apply` command. - `tag_type` - (Optional, String) Type of the tag. Supported values are: `user` or `access`. The default value is user. - `tags` - (Required, Array of strings) List of tags associated with resource instance. - `replace` - (Optional, Bool) If true, it indicates that the attaching operation is a replacement operation From d900f7d9b710e2d0d371f9781ab8f8d5e7abddf6 Mon Sep 17 00:00:00 2001 From: Luca Ioffredo Date: Mon, 7 Oct 2024 09:47:39 +0200 Subject: [PATCH 15/15] Fix PR --- website/docs/r/resource_tag.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/resource_tag.html.markdown b/website/docs/r/resource_tag.html.markdown index 0631c6905c..d2ed232716 100644 --- a/website/docs/r/resource_tag.html.markdown +++ b/website/docs/r/resource_tag.html.markdown @@ -44,7 +44,7 @@ The `ibm_resource_tag` resource provides the following [Timeouts](https://www.te Review the argument references that you can specify for your resource. - `resource_id` - (Required, String) The CRN of the resource on which the tags is be attached. -- `resource_type` - (Optional, String) The resource type on which the tags should be attached. This is valid for SoftLayer (IMS) resources only. The resource_type allowed values are: `SoftLayer_Virtual_DedicatedHost`, `SoftLayer_Hardware`, `SoftLayer_Hardware_Server`, `SoftLayer_Network_Application_Delivery_Controller`, `SoftLayer_Network_Vlan`, `SoftLayer_Network_Vlan_Firewall`, `SoftLayer_Network_Component_Firewall`, `SoftLayer_Network_Firewall_Module_Context`, `SoftLayer_Virtual_Guest`. A wrong value would result in an error in the `terraform apply` command. +- `resource_type` - (Optional, String) The resource type on which the tags should be attached. This is valid for Classic Infrastructure resources only. The `resource_type` allowed values are: `SoftLayer_Virtual_DedicatedHost`, `SoftLayer_Hardware`, `SoftLayer_Hardware_Server`, `SoftLayer_Network_Application_Delivery_Controller`, `SoftLayer_Network_Vlan`, `SoftLayer_Network_Vlan_Firewall`, `SoftLayer_Network_Component_Firewall`, `SoftLayer_Network_Firewall_Module_Context`, `SoftLayer_Virtual_Guest`. A wrong value would result in an error in the `terraform apply` command. - `tag_type` - (Optional, String) Type of the tag. Supported values are: `user` or `access`. The default value is user. - `tags` - (Required, Array of strings) List of tags associated with resource instance. - `replace` - (Optional, Bool) If true, it indicates that the attaching operation is a replacement operation