diff --git a/ibm/service/power/data_source_ibm_pi_instance_volumes.go b/ibm/service/power/data_source_ibm_pi_instance_volumes.go index b0ceedf9eb..050f08f4ea 100644 --- a/ibm/service/power/data_source_ibm_pi_instance_volumes.go +++ b/ibm/service/power/data_source_ibm_pi_instance_volumes.go @@ -5,10 +5,12 @@ package power import ( "context" + "log" "github.com/IBM-Cloud/power-go-client/clients/instance" "github.com/IBM-Cloud/power-go-client/power/models" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" "github.com/hashicorp/go-uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -49,6 +51,11 @@ func DataSourceIBMPIInstanceVolumes() *schema.Resource { Description: "Indicates if the volume is boot capable.", Type: schema.TypeBool, }, + Attr_CRN: { + Computed: true, + Description: "The CRN of this resource.", + Type: schema.TypeString, + }, Attr_Href: { Computed: true, Description: "The hyper link of the volume.", @@ -89,6 +96,13 @@ func DataSourceIBMPIInstanceVolumes() *schema.Resource { Description: "The disk type that is used for this volume.", Type: schema.TypeString, }, + Attr_UserTags: { + Computed: true, + Description: "List of user tags attached to the resource.", + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + Type: schema.TypeSet, + }, }, }, Type: schema.TypeList, @@ -114,12 +128,12 @@ func dataSourceIBMPIInstanceVolumesRead(ctx context.Context, d *schema.ResourceD var clientgenU, _ = uuid.GenerateUUID() d.SetId(clientgenU) d.Set(Attr_BootVolumeID, *volumedata.Volumes[0].VolumeID) - d.Set(Attr_InstanceVolumes, flattenVolumesInstances(volumedata.Volumes)) + d.Set(Attr_InstanceVolumes, flattenVolumesInstances(volumedata.Volumes, meta)) return nil } -func flattenVolumesInstances(list []*models.VolumeReference) []map[string]interface{} { +func flattenVolumesInstances(list []*models.VolumeReference, meta interface{}) []map[string]interface{} { result := make([]map[string]interface{}, 0, len(list)) for _, i := range list { l := map[string]interface{}{ @@ -133,6 +147,14 @@ func flattenVolumesInstances(list []*models.VolumeReference) []map[string]interf Attr_State: *i.State, Attr_Type: *i.DiskType, } + if i.Crn != "" { + l[Attr_CRN] = i.Crn + tags, err := flex.GetGlobalTagsUsingCRN(meta, string(i.Crn), "", UserTagType) + if err != nil { + log.Printf("Error on get of volume (%s) user_tags: %s", *i.VolumeID, err) + } + l[Attr_UserTags] = tags + } result = append(result, l) } return result diff --git a/ibm/service/power/data_source_ibm_pi_volume.go b/ibm/service/power/data_source_ibm_pi_volume.go index 6e1bf43c8d..a4e2d1b4b3 100644 --- a/ibm/service/power/data_source_ibm_pi_volume.go +++ b/ibm/service/power/data_source_ibm_pi_volume.go @@ -5,9 +5,11 @@ package power import ( "context" + "log" "github.com/IBM-Cloud/power-go-client/clients/instance" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -47,6 +49,11 @@ func DataSourceIBMPIVolume() *schema.Resource { Description: "Indicates if the volume is boot capable.", Type: schema.TypeBool, }, + Attr_CRN: { + Computed: true, + Description: "The CRN of this resource.", + Type: schema.TypeString, + }, Attr_ConsistencyGroupName: { Computed: true, Description: "Consistency group name if volume is a part of volume group.", @@ -112,6 +119,13 @@ func DataSourceIBMPIVolume() *schema.Resource { Description: "The state of the volume.", Type: schema.TypeString, }, + Attr_UserTags: { + Computed: true, + Description: "List of user tags attached to the resource.", + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + Type: schema.TypeSet, + }, Attr_VolumePool: { Computed: true, Description: "Volume pool, name of storage pool where the volume is located.", @@ -144,6 +158,14 @@ func dataSourceIBMPIVolumeRead(ctx context.Context, d *schema.ResourceData, meta d.Set(Attr_AuxiliaryVolumeName, volumedata.AuxVolumeName) d.Set(Attr_Bootable, volumedata.Bootable) d.Set(Attr_ConsistencyGroupName, volumedata.ConsistencyGroupName) + if volumedata.Crn != "" { + d.Set(Attr_CRN, volumedata.Crn) + tags, err := flex.GetTagsUsingCRN(meta, string(volumedata.Crn)) + if err != nil { + log.Printf("Error on get of pi volume (%s) user_tags: %s", *volumedata.VolumeID, err) + } + d.Set(Attr_UserTags, tags) + } d.Set(Attr_DiskType, volumedata.DiskType) d.Set(Attr_GroupID, volumedata.GroupID) d.Set(Attr_IOThrottleRate, volumedata.IoThrottleRate) diff --git a/ibm/service/power/data_source_ibm_pi_volume_test.go b/ibm/service/power/data_source_ibm_pi_volume_test.go index a3b8498b7a..f40fb2dca8 100644 --- a/ibm/service/power/data_source_ibm_pi_volume_test.go +++ b/ibm/service/power/data_source_ibm_pi_volume_test.go @@ -13,6 +13,7 @@ import ( ) func TestAccIBMPIVolumeDataSource_basic(t *testing.T) { + volumeRes := "data.ibm_pi_volume.testacc_ds_volume" resource.Test(t, resource.TestCase{ PreCheck: func() { acc.TestAccPreCheck(t) }, Providers: acc.TestAccProviders, @@ -20,7 +21,7 @@ func TestAccIBMPIVolumeDataSource_basic(t *testing.T) { { Config: testAccCheckIBMPIVolumeDataSourceConfig(), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("data.ibm_pi_volume.testacc_ds_volume", "id"), + resource.TestCheckResourceAttrSet(volumeRes, "id"), ), }, }, diff --git a/ibm/service/power/resource_ibm_pi_volume.go b/ibm/service/power/resource_ibm_pi_volume.go index 7810c6f67e..9a60e6ca08 100644 --- a/ibm/service/power/resource_ibm_pi_volume.go +++ b/ibm/service/power/resource_ibm_pi_volume.go @@ -87,6 +87,13 @@ func ResourceIBMPIVolume() *schema.Resource { Optional: true, Type: schema.TypeBool, }, + Arg_UserTags: { + Description: "The user tags attached to this resource.", + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Set: schema.HashString, + Type: schema.TypeSet, + }, Arg_VolumeName: { Description: "The name of the volume.", Required: true, @@ -136,6 +143,11 @@ func ResourceIBMPIVolume() *schema.Resource { Description: "The consistency group name if volume is a part of volume group.", Type: schema.TypeString, }, + Attr_CRN: { + Computed: true, + Description: "The CRN of this resource.", + Type: schema.TypeString, + }, Attr_DeleteOnTermination: { Computed: true, Description: "Indicates if the volume should be deleted when the server terminates.", @@ -265,6 +277,9 @@ func resourceIBMPIVolumeCreate(ctx context.Context, d *schema.ResourceData, meta } } + if v, ok := d.GetOk(Arg_UserTags); ok { + body.UserTags = flex.FlattenSet(v.(*schema.Set)) + } client := instance.NewIBMPIVolumeClient(ctx, sess, cloudInstanceID) vol, err := client.CreateVolume(body) @@ -280,6 +295,14 @@ func resourceIBMPIVolumeCreate(ctx context.Context, d *schema.ResourceData, meta return diag.FromErr(err) } + if _, ok := d.GetOk(Arg_UserTags); ok { + oldList, newList := d.GetChange(Arg_UserTags) + err := flex.UpdateGlobalTagsUsingCRN(oldList, newList, meta, string(vol.Crn), "", UserTagType) + if err != nil { + log.Printf("Error on update of volume (%s) pi_user_tags during creation: %s", volumeid, err) + } + } + return resourceIBMPIVolumeRead(ctx, d, meta) } @@ -304,6 +327,14 @@ func resourceIBMPIVolumeRead(ctx context.Context, d *schema.ResourceData, meta i if vol.VolumeID != nil { d.Set(Attr_VolumeID, vol.VolumeID) } + if vol.Crn != "" { + d.Set(Attr_CRN, vol.Crn) + tags, err := flex.GetGlobalTagsUsingCRN(meta, string(vol.Crn), "", UserTagType) + if err != nil { + log.Printf("Error on get of volume (%s) pi_user_tags: %s", *vol.VolumeID, err) + } + d.Set(Arg_UserTags, tags) + } d.Set(Arg_VolumeName, vol.Name) d.Set(Arg_VolumePool, vol.VolumePool) if vol.Shareable != nil { @@ -383,6 +414,16 @@ func resourceIBMPIVolumeUpdate(ctx context.Context, d *schema.ResourceData, meta } } + if d.HasChange(Arg_UserTags) { + crn := d.Get(Attr_CRN) + if crn != nil && crn != "" { + oldList, newList := d.GetChange(Arg_UserTags) + err := flex.UpdateGlobalTagsUsingCRN(oldList, newList, meta, crn.(string), "", UserTagType) + if err != nil { + log.Printf("Error on update of pi volume (%s) pi_user_tags: %s", volumeID, err) + } + } + } return resourceIBMPIVolumeRead(ctx, d, meta) } diff --git a/ibm/service/power/resource_ibm_pi_volume_clone.go b/ibm/service/power/resource_ibm_pi_volume_clone.go index f2a1d33ea2..37bd407c8b 100644 --- a/ibm/service/power/resource_ibm_pi_volume_clone.go +++ b/ibm/service/power/resource_ibm_pi_volume_clone.go @@ -52,6 +52,14 @@ func ResourceIBMPIVolumeClone() *schema.Resource { Optional: true, Type: schema.TypeBool, }, + Arg_UserTags: { + Description: "The user tags attached to this resource.", + Elem: &schema.Schema{Type: schema.TypeString}, + ForceNew: true, + Optional: true, + Set: schema.HashString, + Type: schema.TypeSet, + }, Arg_VolumeCloneName: { Description: "The base name of the newly cloned volume(s).", ForceNew: true, @@ -135,6 +143,10 @@ func resourceIBMPIVolumeCloneCreate(ctx context.Context, d *schema.ResourceData, body.TargetReplicationEnabled = flex.PtrToBool(d.Get(Arg_ReplicationEnabled).(bool)) } + if v, ok := d.GetOk(Arg_UserTags); ok { + body.UserTags = flex.FlattenSet(v.(*schema.Set)) + } + client := instance.NewIBMPICloneVolumeClient(ctx, sess, cloudInstanceID) volClone, err := client.Create(body) if err != nil { diff --git a/ibm/service/power/resource_ibm_pi_volume_test.go b/ibm/service/power/resource_ibm_pi_volume_test.go index 74ed0d8e97..dc149fc974 100644 --- a/ibm/service/power/resource_ibm_pi_volume_test.go +++ b/ibm/service/power/resource_ibm_pi_volume_test.go @@ -21,6 +21,7 @@ import ( func TestAccIBMPIVolumebasic(t *testing.T) { name := fmt.Sprintf("tf-pi-volume-%d", acctest.RandIntRange(10, 100)) + volumeRes := "ibm_pi_volume.power_volume" resource.Test(t, resource.TestCase{ PreCheck: func() { acc.TestAccPreCheck(t) }, Providers: acc.TestAccProviders, @@ -29,19 +30,16 @@ func TestAccIBMPIVolumebasic(t *testing.T) { { Config: testAccCheckIBMPIVolumeConfig(name), Check: resource.ComposeTestCheckFunc( - testAccCheckIBMPIVolumeExists("ibm_pi_volume.power_volume"), - resource.TestCheckResourceAttr( - "ibm_pi_volume.power_volume", "pi_volume_name", name), + testAccCheckIBMPIVolumeExists(volumeRes), + resource.TestCheckResourceAttr(volumeRes, "pi_volume_name", name), ), }, { Config: testAccCheckIBMPIVolumeSizeConfig(name), Check: resource.ComposeTestCheckFunc( - testAccCheckIBMPIVolumeExists("ibm_pi_volume.power_volume"), - resource.TestCheckResourceAttr( - "ibm_pi_volume.power_volume", "pi_volume_name", name), - resource.TestCheckResourceAttr( - "ibm_pi_volume.power_volume", "pi_volume_size", "30"), + testAccCheckIBMPIVolumeExists(volumeRes), + resource.TestCheckResourceAttr(volumeRes, "pi_volume_name", name), + resource.TestCheckResourceAttr(volumeRes, "pi_volume_size", "30"), ), }, }, @@ -260,3 +258,50 @@ func testAccCheckIBMPIVolumeUpdateBasicConfig(name, piCloudInstanceId, piStorage pi_volume_type = "%[4]v" }`, name, piCloudInstanceId, piStoragePool, piStorageType) } + +func TestAccIBMPIVolumeUserTags(t *testing.T) { + name := fmt.Sprintf("tf-pi-volume-%d", acctest.RandIntRange(10, 100)) + volumeRes := "ibm_pi_volume.power_volume" + userTagsString := `["env:dev","test_tag"]` + userTagsStringUpdated := `["env:dev","test_tag","test_tag2"]` + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMPIVolumeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMPIVolumeUserTagsConfig(name, userTagsString), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMPIVolumeExists(volumeRes), + resource.TestCheckResourceAttr(volumeRes, "pi_volume_name", name), + resource.TestCheckResourceAttr(volumeRes, "pi_user_tags.#", "2"), + resource.TestCheckTypeSetElemAttr(volumeRes, "pi_user_tags.*", "env:dev"), + resource.TestCheckTypeSetElemAttr(volumeRes, "pi_user_tags.*", "test_tag"), + ), + }, + { + Config: testAccCheckIBMPIVolumeUserTagsConfig(name, userTagsStringUpdated), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMPIVolumeExists(volumeRes), + resource.TestCheckResourceAttr(volumeRes, "pi_volume_name", name), + resource.TestCheckResourceAttr(volumeRes, "pi_user_tags.#", "3"), + resource.TestCheckTypeSetElemAttr(volumeRes, "pi_user_tags.*", "env:dev"), + resource.TestCheckTypeSetElemAttr(volumeRes, "pi_user_tags.*", "test_tag"), + resource.TestCheckTypeSetElemAttr(volumeRes, "pi_user_tags.*", "test_tag2"), + ), + }, + }, + }) +} + +func testAccCheckIBMPIVolumeUserTagsConfig(name string, userTagsString string) string { + return fmt.Sprintf(` + resource "ibm_pi_volume" "power_volume" { + pi_cloud_instance_id = "%[2]s" + pi_volume_name = "%[1]s" + pi_volume_shareable = true + pi_volume_size = 20 + pi_volume_type = "tier1" + pi_user_tags = %[3]s + }`, name, acc.Pi_cloud_instance_id, userTagsString) +} diff --git a/website/docs/d/pi_instance_volumes.html.markdown b/website/docs/d/pi_instance_volumes.html.markdown index 16f17cfdb0..087efdf872 100644 --- a/website/docs/d/pi_instance_volumes.html.markdown +++ b/website/docs/d/pi_instance_volumes.html.markdown @@ -53,6 +53,7 @@ In addition to all argument reference list, you can access the following attribu Nested scheme for `instance_volumes`: - `bootable`- (Boolean) Indicates if the volume is boot capable. + - `crn` - (String) The CRN of this resource. - `href` - (String) The hyper link of the volume. - `id` - (String) The unique identifier of the volume. - `name` - (String) The name of the volume. @@ -61,3 +62,4 @@ In addition to all argument reference list, you can access the following attribu - `size` - (Integer) The size of this volume in GB. - `state` - (String) The state of the volume. - `type` - (String) The disk type that is used for this volume. + - `user_tags` - (List) List of user tags attached to the resource. diff --git a/website/docs/d/pi_volume.html.markdown b/website/docs/d/pi_volume.html.markdown index 2eac4979a9..6e927f054a 100644 --- a/website/docs/d/pi_volume.html.markdown +++ b/website/docs/d/pi_volume.html.markdown @@ -52,6 +52,7 @@ In addition to all argument reference list, you can access the following attribu - `auxiliary_volume_name` - (String) The auxiliary volume name. - `bootable` - (Boolean) Indicates if the volume is boot capable. - `consistency_group_name` - (String) Consistency group name if volume is a part of volume group. +- `crn` - (String) The CRN of this resource. - `disk_type` - (String) The disk type that is used for the volume. - `group_id` - (String) The volume group id in which the volume belongs. - `id` - (String) The unique identifier of the volume. @@ -65,5 +66,7 @@ In addition to all argument reference list, you can access the following attribu - `shareable` - (String) Indicates if the volume is shareable between VMs. - `size` - (Integer) The size of the volume in GB. - `state` - (String) The state of the volume. +- `user_tags` - (List) List of user tags attached to the resource. - `volume_pool` - (String) Volume pool, name of storage pool where the volume is located. - `wwn` - (String) The world wide name of the volume. + diff --git a/website/docs/r/pi_volume.html.markdown b/website/docs/r/pi_volume.html.markdown index c75ab9b075..82206410b0 100644 --- a/website/docs/r/pi_volume.html.markdown +++ b/website/docs/r/pi_volume.html.markdown @@ -59,6 +59,7 @@ Review the argument references that you can specify for your resource. - `pi_anti_affinity_volumes`- (Optional, String) List of volumes to base volume anti-affinity policy against; required if requesting `anti-affinity` and `pi_anti_affinity_instances` is not provided. - `pi_cloud_instance_id` - (Required, String) The GUID of the service instance associated with an account. - `pi_replication_enabled` - (Optional, Boolean) Indicates if the volume should be replication enabled or not. +- `pi_user_tags` - (Optional, List) The user tags attached to this resource. - `pi_volume_name` - (Required, String) The name of the volume. - `pi_volume_pool` - (Optional, String) Volume pool where the volume will be created; if provided then `pi_affinity_policy` values will be ignored. - `pi_volume_shareable` - (Required, Boolean) If set to **true**, the volume can be shared across Power Systems Virtual Server instances. If set to **false**, you can attach it only to one instance. @@ -72,6 +73,7 @@ In addition to all argument reference list, you can access the following attribu - `auxiliary` - (Boolean) Indicates if the volume is auxiliary or not. - `auxiliary_volume_name` - (String) The auxiliary volume name. - `consistency_group_name` - (String) The consistency group name if volume is a part of volume group. +- `crn` - (String) The CRN of this resource. - `delete_on_termination` - (Boolean) Indicates if the volume should be deleted when the server terminates. - `group_id` - (String) The volume group id to which volume belongs. - `id` - (String) The unique identifier of the volume. The ID is composed of `/`. diff --git a/website/docs/r/pi_volume_clone.html.markdown b/website/docs/r/pi_volume_clone.html.markdown index 6eb0ed1ee1..aa0637742f 100644 --- a/website/docs/r/pi_volume_clone.html.markdown +++ b/website/docs/r/pi_volume_clone.html.markdown @@ -54,6 +54,7 @@ Review the argument references that you can specify for your resource. - `pi_cloud_instance_id` - (Required, String) The GUID of the service instance associated with an account. - `pi_replication_enabled` - (Optional, Boolean) Indicates whether the cloned volume should have replication enabled. If no value is provided, it will default to the replication status of the source volume(s). - `pi_target_storage_tier` - (Optional, String) The storage tier for the cloned volume(s). To get a list of available storage tiers, please use the [ibm_pi_storage_types_capacity](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/pi_storage_types_capacity) data source. +- `pi_user_tags` - (Optional, List) The user tags attached to this resource. - `pi_volume_clone_name` - (Required, String) The base name of the newly cloned volume(s). - `pi_volume_ids` - (Required, Set of String) List of volumes to be cloned.