From 86516193c0eb56ad00097bf68b704f52bf467e20 Mon Sep 17 00:00:00 2001 From: Deepak Selvakumar <77007253+deepaksibm@users.noreply.github.com> Date: Tue, 13 Jun 2023 17:28:46 +0530 Subject: [PATCH] File share Mount target resource rename (#4604) * Deprecated share target and added share mount target resources and data sources * added maturity * updated beta sdk version and removed custom sdk * updated read methods * pr review comments fix * fix for concurrent target create and delete issue --- examples/ibm-is-ng/main.tf | 15 + go.mod | 2 +- go.sum | 5 +- ibm/provider/provider.go | 4 + ibm/service/vpc/data_source_ibm_is_share.go | 4 +- .../data_source_ibm_is_share_mount_target.go | 305 ++++++++++++++ ...a_source_ibm_is_share_mount_target_test.go | 48 +++ .../data_source_ibm_is_share_mount_targets.go | 324 +++++++++++++++ ..._source_ibm_is_share_mount_targets_test.go | 63 +++ .../vpc/data_source_ibm_is_share_target.go | 5 +- .../vpc/data_source_ibm_is_share_targets.go | 7 +- ibm/service/vpc/data_source_ibm_is_shares.go | 4 +- .../vpc/data_source_ibm_is_source_share.go | 4 +- ibm/service/vpc/resource_ibm_is_share.go | 18 +- .../vpc/resource_ibm_is_share_mount_target.go | 376 ++++++++++++++++++ ...resource_ibm_is_share_mount_target_test.go | 133 +++++++ .../vpc/resource_ibm_is_share_target.go | 11 +- .../d/is_share_mount_target.html.markdown | 72 ++++ .../d/is_share_mount_targets.html.markdown | 67 ++++ website/docs/d/is_share_target.html.markdown | 2 + website/docs/d/is_share_targets.html.markdown | 3 +- .../r/is_share_mount_target.html.markdown | 77 ++++ website/docs/r/is_share_target.html.markdown | 3 + 23 files changed, 1522 insertions(+), 30 deletions(-) create mode 100644 ibm/service/vpc/data_source_ibm_is_share_mount_target.go create mode 100644 ibm/service/vpc/data_source_ibm_is_share_mount_target_test.go create mode 100644 ibm/service/vpc/data_source_ibm_is_share_mount_targets.go create mode 100644 ibm/service/vpc/data_source_ibm_is_share_mount_targets_test.go create mode 100644 ibm/service/vpc/resource_ibm_is_share_mount_target.go create mode 100644 ibm/service/vpc/resource_ibm_is_share_mount_target_test.go create mode 100644 website/docs/d/is_share_mount_target.html.markdown create mode 100644 website/docs/d/is_share_mount_targets.html.markdown create mode 100644 website/docs/r/is_share_mount_target.html.markdown diff --git a/examples/ibm-is-ng/main.tf b/examples/ibm-is-ng/main.tf index c6086babe2..528541aec5 100644 --- a/examples/ibm-is-ng/main.tf +++ b/examples/ibm-is-ng/main.tf @@ -1251,6 +1251,21 @@ data "ibm_is_share_targets" "is_share_targets" { share = ibm_is_share.is_share.id } +resource "ibm_is_share_mount_target" "is_share_mount_target" { + share = ibm_is_share.is_share.id + vpc = ibm_is_vpc.vpc1.id + name = "my-share-target-1" +} + +data "ibm_is_share_mount_target" "is_share_mount_target" { + share = ibm_is_share.is_share.id + mount_target = ibm_is_share_mount_target.is_share_target.mount_target +} + +data "ibm_is_share_mount_targets" "is_share_mount_targets" { + share = ibm_is_share.is_share.id +} + data "ibm_is_share" "is_share" { share = ibm_is_share.is_share.id } diff --git a/go.mod b/go.mod index 7e33a680de..0e654fd750 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/IBM/scc-go-sdk/v4 v4.0.2 github.com/IBM/schematics-go-sdk v0.2.1 github.com/IBM/secrets-manager-go-sdk/v2 v2.0.0 - github.com/IBM/vpc-beta-go-sdk v0.1.0 + github.com/IBM/vpc-beta-go-sdk v0.4.0 github.com/IBM/vpc-go-sdk v0.38.0 github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5 github.com/Shopify/sarama v1.29.1 diff --git a/go.sum b/go.sum index be3342fb70..d1544b9a88 100644 --- a/go.sum +++ b/go.sum @@ -103,8 +103,8 @@ github.com/IBM/schematics-go-sdk v0.2.1 h1:byATysGD+Z1k/wdtNqQmKALcAPjgSLuSyzcab github.com/IBM/schematics-go-sdk v0.2.1/go.mod h1:Tw2OSAPdpC69AxcwoyqcYYaGTTW6YpERF9uNEU+BFRQ= github.com/IBM/secrets-manager-go-sdk/v2 v2.0.0 h1:Lx4Bvim/MfoHEYR+n312bty5DirAJypBGGS9YZo3zCw= github.com/IBM/secrets-manager-go-sdk/v2 v2.0.0/go.mod h1:jagqWmjZ0zUEqh5jdGB42ApSQS40fu2LWw6pdg8JJko= -github.com/IBM/vpc-beta-go-sdk v0.1.0 h1:+kdF+Y/0KY189HhpkqDrue9o0LluAr7rlOU5Zhu7hck= -github.com/IBM/vpc-beta-go-sdk v0.1.0/go.mod h1:TGfLgJVmgQy6XvXc4lya8Vxvw7fvk83nqs5hdP+/VGo= +github.com/IBM/vpc-beta-go-sdk v0.4.0 h1:OxABcc2GeclCMN/bpcQfM2cmNB2GuajwuocsPJSFdl0= +github.com/IBM/vpc-beta-go-sdk v0.4.0/go.mod h1:fzHDAQIqH/5yJmYsKodKHLcqxMDT+yfH6vZjdiw8CQA= github.com/IBM/vpc-go-sdk v0.38.0 h1:iB2/ukDciK6s2VBnMmNQeZqenOaUXJSt+V2M4fMjAnM= github.com/IBM/vpc-go-sdk v0.38.0/go.mod h1:MgZrbITC067AlcE5oy4hwylasFvrePL4RVxeF6GTdKQ= github.com/Logicalis/asn1 v0.0.0-20190312173541-d60463189a56 h1:vuquMR410psHNax14XKNWa0Ae/kYgWJcXi0IFuX60N0= @@ -686,7 +686,6 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/onsi/gomega v1.18.0/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.20.0/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= diff --git a/ibm/provider/provider.go b/ibm/provider/provider.go index 645bd06f00..1a878f8c2e 100644 --- a/ibm/provider/provider.go +++ b/ibm/provider/provider.go @@ -473,6 +473,8 @@ func Provider() *schema.Provider { "ibm_is_share_profiles": vpc.DataSourceIbmIsShareProfiles(), "ibm_is_share_target": vpc.DataSourceIbmIsShareTarget(), "ibm_is_share_targets": vpc.DataSourceIbmIsShareTargets(), + "ibm_is_share_mount_target": vpc.DataSourceIBMIsShareTarget(), + "ibm_is_share_mount_targets": vpc.DataSourceIBMIsShareTargets(), "ibm_is_volume": vpc.DataSourceIBMISVolume(), "ibm_is_volumes": vpc.DataSourceIBMIsVolumes(), "ibm_is_volume_profile": vpc.DataSourceIBMISVolumeProfile(), @@ -1003,6 +1005,7 @@ func Provider() *schema.Provider { "ibm_is_share": vpc.ResourceIbmIsShare(), "ibm_is_share_replica_operations": vpc.ResourceIbmIsShareReplicaOperations(), "ibm_is_share_target": vpc.ResourceIbmIsShareMountTarget(), + "ibm_is_share_mount_target": vpc.ResourceIBMIsShareMountTarget(), "ibm_is_subnet": vpc.ResourceIBMISSubnet(), "ibm_is_subnet_reserved_ip": vpc.ResourceIBMISReservedIP(), "ibm_is_subnet_network_acl_attachment": vpc.ResourceIBMISSubnetNetworkACLAttachment(), @@ -1402,6 +1405,7 @@ func Validator() validate.ValidatorDict { "ibm_is_share": vpc.ResourceIbmIsShareValidator(), "ibm_is_share_replica_operations": vpc.ResourceIbmIsShareReplicaOperationsValidator(), "ibm_is_share_target": vpc.ResourceIbmIsShareMountTargetValidator(), + "ibm_is_share_mount_target": vpc.ResourceIBMIsShareMountTargetValidator(), "ibm_is_snapshot": vpc.ResourceIBMISSnapshotValidator(), "ibm_is_ssh_key": vpc.ResourceIBMISSHKeyValidator(), "ibm_is_subnet": vpc.ResourceIBMISSubnetValidator(), diff --git a/ibm/service/vpc/data_source_ibm_is_share.go b/ibm/service/vpc/data_source_ibm_is_share.go index 21596fbc3f..6516968004 100644 --- a/ibm/service/vpc/data_source_ibm_is_share.go +++ b/ibm/service/vpc/data_source_ibm_is_share.go @@ -468,8 +468,8 @@ func dataSourceIbmIsShareRead(context context.Context, d *schema.ResourceData, m return diag.FromErr(fmt.Errorf("Error setting source_share %s", err)) } } - if share.Targets != nil { - err = d.Set("share_targets", dataSourceShareFlattenTargets(share.Targets)) + if share.MountTargets != nil { + err = d.Set("share_targets", dataSourceShareFlattenTargets(share.MountTargets)) if err != nil { return diag.FromErr(fmt.Errorf("Error setting targets %s", err)) } diff --git a/ibm/service/vpc/data_source_ibm_is_share_mount_target.go b/ibm/service/vpc/data_source_ibm_is_share_mount_target.go new file mode 100644 index 0000000000..f6f42a96a6 --- /dev/null +++ b/ibm/service/vpc/data_source_ibm_is_share_mount_target.go @@ -0,0 +1,305 @@ +// Copyright IBM Corp. 2021 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +package vpc + +import ( + "context" + "fmt" + "log" + + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM/vpc-beta-go-sdk/vpcbetav1" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSourceIBMIsShareTarget() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceIBMIsShareTargetRead, + + Schema: map[string]*schema.Schema{ + "share": { + Type: schema.TypeString, + Optional: true, + Description: "The file share identifier.", + }, + "mount_target": { + Type: schema.TypeString, + Optional: true, + Description: "The share target identifier.", + }, + "share_name": { + Type: schema.TypeString, + Optional: true, + ExactlyOneOf: []string{"share", "share_name"}, + Description: "The file share name.", + }, + "mount_target_name": { + Type: schema.TypeString, + Optional: true, + ExactlyOneOf: []string{"mount_target", "mount_target_name"}, + Description: "The share target name.", + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: "The date and time that the share target was created.", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "The URL for this share target.", + }, + "lifecycle_state": { + Type: schema.TypeString, + Computed: true, + Description: "The lifecycle state of the mount target.", + }, + "mount_path": { + Type: schema.TypeString, + Computed: true, + Description: "The mount path for the share.The IP addresses used in the mount path are currently within the IBM services IP range, but are expected to change to be within one of the VPC's subnets in the future.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The user-defined name for this share target.", + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of resource referenced.", + }, + "subnet": { + Type: schema.TypeList, + Computed: true, + Description: "The subnet associated with this file share target.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "crn": { + Type: schema.TypeString, + Computed: true, + Description: "The CRN for this subnet.", + }, + "deleted": { + Type: schema.TypeList, + Computed: true, + Description: "If present, this property indicates the referenced resource has been deleted and providessome supplementary information.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "more_info": { + Type: schema.TypeString, + Computed: true, + Description: "Link to documentation about deleted resources.", + }, + }, + }, + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "The URL for this subnet.", + }, + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The unique identifier for this subnet.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The user-defined name for this subnet.", + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + Description: "The resource type.", + }, + }, + }, + }, + "vpc": { + Type: schema.TypeList, + Computed: true, + Description: "The VPC to which this share target is allowing to mount the file share.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "crn": { + Type: schema.TypeString, + Computed: true, + Description: "The CRN for this VPC.", + }, + "deleted": { + Type: schema.TypeList, + Computed: true, + Description: "If present, this property indicates the referenced resource has been deleted and providessome supplementary information.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "more_info": { + Type: schema.TypeString, + Computed: true, + Description: "Link to documentation about deleted resources.", + }, + }, + }, + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "The URL for this VPC.", + }, + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The unique identifier for this VPC.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The unique user-defined name for this VPC.", + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + Description: "The resource type.", + }, + }, + }, + }, + }, + } +} + +func dataSourceIBMIsShareTargetRead(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + vpcClient, err := meta.(conns.ClientSession).VpcV1BetaAPI() + if err != nil { + return diag.FromErr(err) + } + + share_id := d.Get("share").(string) + share_name := d.Get("share_name").(string) + share_target := d.Get("mount_target").(string) + share_target_name := d.Get("mount_target_name").(string) + var shareTarget *vpcbetav1.ShareMountTarget + if share_name != "" { + listSharesOptions := &vpcbetav1.ListSharesOptions{} + listSharesOptions.Name = &share_name + shareCollection, response, err := vpcClient.ListSharesWithContext(context, listSharesOptions) + if err != nil { + log.Printf("[DEBUG] ListSharesWithContext failed %s\n%s", err, response) + return diag.FromErr(err) + } + for _, sharesItem := range shareCollection.Shares { + if *sharesItem.Name == share_name { + share_id = *sharesItem.ID + break + } + } + } + if share_target_name != "" { + listShareTargetsOptions := &vpcbetav1.ListShareMountTargetsOptions{} + + listShareTargetsOptions.SetShareID(share_id) + listShareTargetsOptions.SetName(share_target_name) + + shareTargetCollection, response, err := vpcClient.ListShareMountTargetsWithContext(context, listShareTargetsOptions) + if err != nil { + log.Printf("[DEBUG] ListShareTargetsWithContext failed %s\n%s", err, response) + return diag.FromErr(err) + } + for _, targetsItem := range shareTargetCollection.MountTargets { + if *targetsItem.Name == share_target_name { + shareTarget = &targetsItem + break + } + } + } else { + getShareTargetOptions := &vpcbetav1.GetShareMountTargetOptions{} + getShareTargetOptions.SetShareID(share_id) + getShareTargetOptions.SetID(share_target) + shareTarget1, response, err := vpcClient.GetShareMountTargetWithContext(context, getShareTargetOptions) + if err != nil { + log.Printf("[DEBUG] GetShareTargetWithContext failed %s\n%s", err, response) + return diag.FromErr(err) + } + shareTarget = shareTarget1 + } + + d.SetId(fmt.Sprintf("%s/%s", share_id, *shareTarget.ID)) + if err = d.Set("created_at", shareTarget.CreatedAt.String()); err != nil { + return diag.FromErr(fmt.Errorf("Error setting created_at: %s", err)) + } + if err = d.Set("href", shareTarget.Href); err != nil { + return diag.FromErr(fmt.Errorf("Error setting href: %s", err)) + } + if err = d.Set("lifecycle_state", shareTarget.LifecycleState); err != nil { + return diag.FromErr(fmt.Errorf("Error setting lifecycle_state: %s", err)) + } + if err = d.Set("mount_path", shareTarget.MountPath); err != nil { + return diag.FromErr(fmt.Errorf("Error setting mount_path: %s", err)) + } + if err = d.Set("name", shareTarget.Name); err != nil { + return diag.FromErr(fmt.Errorf("Error setting name: %s", err)) + } + if err = d.Set("resource_type", shareTarget.ResourceType); err != nil { + return diag.FromErr(fmt.Errorf("Error setting resource_type: %s", err)) + } + + if shareTarget.VPC != nil { + err = d.Set("vpc", dataSourceShareMountTargetFlattenVpc(*shareTarget.VPC)) + if err != nil { + return diag.FromErr(fmt.Errorf("Error setting vpc %s", err)) + } + } + + return nil +} + +func dataSourceShareMountTargetFlattenVpc(result vpcbetav1.VPCReference) (finalList []map[string]interface{}) { + finalList = []map[string]interface{}{} + finalMap := dataSourceShareMountTargetVpcToMap(result) + finalList = append(finalList, finalMap) + + return finalList +} + +func dataSourceShareMountTargetVpcToMap(vpcItem vpcbetav1.VPCReference) (vpcMap map[string]interface{}) { + vpcMap = map[string]interface{}{} + + if vpcItem.CRN != nil { + vpcMap["crn"] = vpcItem.CRN + } + if vpcItem.Deleted != nil { + deletedList := []map[string]interface{}{} + deletedMap := dataSourceShareTargetVpcDeletedToMap(*vpcItem.Deleted) + deletedList = append(deletedList, deletedMap) + vpcMap["deleted"] = deletedList + } + if vpcItem.Href != nil { + vpcMap["href"] = vpcItem.Href + } + if vpcItem.ID != nil { + vpcMap["id"] = vpcItem.ID + } + if vpcItem.Name != nil { + vpcMap["name"] = vpcItem.Name + } + /* + if vpcItem.ResourceType != nil { + vpcMap["resource_type"] = vpcItem.ResourceType + } + */ + + return vpcMap +} + +func dataSourceShareMountTargetVpcDeletedToMap(deletedItem vpcbetav1.VPCReferenceDeleted) (deletedMap map[string]interface{}) { + deletedMap = map[string]interface{}{} + + if deletedItem.MoreInfo != nil { + deletedMap["more_info"] = deletedItem.MoreInfo + } + + return deletedMap +} diff --git a/ibm/service/vpc/data_source_ibm_is_share_mount_target_test.go b/ibm/service/vpc/data_source_ibm_is_share_mount_target_test.go new file mode 100644 index 0000000000..4d3370972d --- /dev/null +++ b/ibm/service/vpc/data_source_ibm_is_share_mount_target_test.go @@ -0,0 +1,48 @@ +// Copyright IBM Corp. 2021 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +package vpc_test + +import ( + "fmt" + "testing" + + acc "github.com/IBM-Cloud/terraform-provider-ibm/ibm/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccIBMIsShareTargetDataSourceAllArgs(t *testing.T) { + vpcName := fmt.Sprintf("tf-vpc-%d", acctest.RandIntRange(10, 100)) + targetName := fmt.Sprintf("tf-share-target-%d", acctest.RandIntRange(10, 100)) + shareName := fmt.Sprintf("tf-fs-name-%d", acctest.RandIntRange(10, 100)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMIsShareTargetDataSourceConfigBasic(shareName, vpcName, targetName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_target.is_share_target", "id"), + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_target.is_share_target", "created_at"), + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_target.is_share_target", "href"), + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_target.is_share_target", "lifecycle_state"), + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_target.is_share_target", "mount_path"), + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_target.is_share_target", "name"), + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_target.is_share_target", "resource_type"), + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_target.is_share_target", "vpc.#"), + ), + }, + }, + }) +} + +func testAccCheckIBMIsShareTargetDataSourceConfigBasic(sname, vpcName, targetName string) string { + return testAccCheckIbmIsShareTargetsDataSourceConfigBasic(sname, vpcName, targetName) + fmt.Sprintf(` + + data "ibm_is_share_mount_target" "is_share_target" { + share = ibm_is_share.is_share.id + share_target = data.ibm_is_mount_share_targets.is_share_targets.mount_targets.0.id + } + `) +} diff --git a/ibm/service/vpc/data_source_ibm_is_share_mount_targets.go b/ibm/service/vpc/data_source_ibm_is_share_mount_targets.go new file mode 100644 index 0000000000..fe03f3aa45 --- /dev/null +++ b/ibm/service/vpc/data_source_ibm_is_share_mount_targets.go @@ -0,0 +1,324 @@ +// Copyright IBM Corp. 2021 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +package vpc + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM/vpc-beta-go-sdk/vpcbetav1" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSourceIBMIsShareTargets() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceIBMIsShareTargetsRead, + + Schema: map[string]*schema.Schema{ + "share": { + Type: schema.TypeString, + Required: true, + Description: "The file share identifier.", + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "The user-defined name for this share target.", + }, + "mount_targets": { + Type: schema.TypeList, + Computed: true, + Description: "Collection of share targets.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The user-defined name for this share target.", + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: "The date and time that the share target was created.", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "The URL for this share target.", + }, + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The unique identifier for this share target.", + }, + "lifecycle_state": { + Type: schema.TypeString, + Computed: true, + Description: "The lifecycle state of the mount target.", + }, + "mount_path": { + Type: schema.TypeString, + Computed: true, + Description: "The mount path for the share.The IP addresses used in the mount path are currently within the IBM services IP range, but are expected to change to be within one of the VPC's subnets in the future.", + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of resource referenced.", + }, + "subnet": { + Type: schema.TypeList, + Computed: true, + Description: "The subnet associated with this file share target.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "crn": { + Type: schema.TypeString, + Computed: true, + Description: "The CRN for this subnet.", + }, + "deleted": { + Type: schema.TypeList, + Computed: true, + Description: "If present, this property indicates the referenced resource has been deleted and providessome supplementary information.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "more_info": { + Type: schema.TypeString, + Computed: true, + Description: "Link to documentation about deleted resources.", + }, + }, + }, + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "The URL for this subnet.", + }, + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The unique identifier for this subnet.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The user-defined name for this subnet.", + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + Description: "The resource type.", + }, + }, + }, + }, + "vpc": { + Type: schema.TypeList, + Computed: true, + Description: "The VPC to which this share target is allowing to mount the file share.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "crn": { + Type: schema.TypeString, + Computed: true, + Description: "The CRN for this VPC.", + }, + "deleted": { + Type: schema.TypeList, + Computed: true, + Description: "If present, this property indicates the referenced resource has been deleted and providessome supplementary information.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "more_info": { + Type: schema.TypeString, + Computed: true, + Description: "Link to documentation about deleted resources.", + }, + }, + }, + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "The URL for this VPC.", + }, + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The unique identifier for this VPC.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The unique user-defined name for this VPC.", + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + Description: "The resource type.", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func dataSourceIBMIsShareTargetsRead(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + vpcClient, err := meta.(conns.ClientSession).VpcV1BetaAPI() + if err != nil { + return diag.FromErr(err) + } + + listShareTargetsOptions := &vpcbetav1.ListShareMountTargetsOptions{} + + listShareTargetsOptions.SetShareID(d.Get("share").(string)) + if name, ok := d.GetOk("name"); ok { + listShareTargetsOptions.SetName(name.(string)) + } + shareTargetCollection, response, err := vpcClient.ListShareMountTargetsWithContext(context, listShareTargetsOptions) + if err != nil { + log.Printf("[DEBUG] ListShareTargetsWithContext failed %s\n%s", err, response) + return diag.FromErr(err) + } + + d.SetId(dataSourceIbmIsShareTargetsID(d)) + + if shareTargetCollection.MountTargets != nil { + err = d.Set("mount_targets", dataSourceShareTargetCollectionFlattenTargets(shareTargetCollection.MountTargets)) + if err != nil { + return diag.FromErr(fmt.Errorf("Error setting targets %s", err)) + } + } + + return nil +} + +// dataSourceIBMIsShareTargetsID returns a reasonable ID for the list. +func dataSourceIBMIsShareTargetsID(d *schema.ResourceData) string { + return time.Now().UTC().String() +} + +func dataSourceShareMountTargetCollectionFlattenTargets(result []vpcbetav1.ShareMountTarget) (targets []map[string]interface{}) { + for _, targetsItem := range result { + targets = append(targets, dataSourceShareTargetCollectionTargetsToMap(targetsItem)) + } + + return targets +} + +func dataSourceShareMountTargetCollectionTargetsToMap(targetsItem vpcbetav1.ShareMountTarget) (targetsMap map[string]interface{}) { + targetsMap = map[string]interface{}{} + + if targetsItem.CreatedAt != nil { + targetsMap["created_at"] = targetsItem.CreatedAt.String() + } + if targetsItem.Href != nil { + targetsMap["href"] = targetsItem.Href + } + if targetsItem.ID != nil { + targetsMap["id"] = targetsItem.ID + } + if targetsItem.LifecycleState != nil { + targetsMap["lifecycle_state"] = targetsItem.LifecycleState + } + if targetsItem.MountPath != nil { + targetsMap["mount_path"] = targetsItem.MountPath + } + if targetsItem.Name != nil { + targetsMap["name"] = targetsItem.Name + } + if targetsItem.ResourceType != nil { + targetsMap["resource_type"] = targetsItem.ResourceType + } + + if targetsItem.VPC.CRN != nil { + vpcList := []map[string]interface{}{} + vpcMap := dataSourceShareTargetCollectionTargetsVpcToMap(*targetsItem.VPC) + vpcList = append(vpcList, vpcMap) + targetsMap["vpc"] = vpcList + } + + return targetsMap +} + +func dataSourceShareMountTargetCollectionTargetsSubnetToMap(subnetItem vpcbetav1.SubnetReference) (subnetMap map[string]interface{}) { + subnetMap = map[string]interface{}{} + + if subnetItem.CRN != nil { + subnetMap["crn"] = subnetItem.CRN + } + if subnetItem.Deleted != nil { + deletedList := []map[string]interface{}{} + deletedMap := dataSourceShareTargetCollectionSubnetDeletedToMap(*subnetItem.Deleted) + deletedList = append(deletedList, deletedMap) + subnetMap["deleted"] = deletedList + } + if subnetItem.Href != nil { + subnetMap["href"] = subnetItem.Href + } + if subnetItem.ID != nil { + subnetMap["id"] = subnetItem.ID + } + if subnetItem.Name != nil { + subnetMap["name"] = subnetItem.Name + } + + return subnetMap +} + +func dataSourceShareMountTargetCollectionSubnetDeletedToMap(deletedItem vpcbetav1.SubnetReferenceDeleted) (deletedMap map[string]interface{}) { + deletedMap = map[string]interface{}{} + + if deletedItem.MoreInfo != nil { + deletedMap["more_info"] = deletedItem.MoreInfo + } + + return deletedMap +} + +func dataSourceShareMountTargetCollectionTargetsVpcToMap(vpcItem vpcbetav1.VPCReference) (vpcMap map[string]interface{}) { + vpcMap = map[string]interface{}{} + + if vpcItem.CRN != nil { + vpcMap["crn"] = vpcItem.CRN + } + if vpcItem.Deleted != nil { + deletedList := []map[string]interface{}{} + deletedMap := dataSourceShareTargetCollectionVpcDeletedToMap(*vpcItem.Deleted) + deletedList = append(deletedList, deletedMap) + vpcMap["deleted"] = deletedList + } + if vpcItem.Href != nil { + vpcMap["href"] = vpcItem.Href + } + if vpcItem.ID != nil { + vpcMap["id"] = vpcItem.ID + } + if vpcItem.Name != nil { + vpcMap["name"] = vpcItem.Name + } + + return vpcMap +} + +func dataSourceShareMountTargetCollectionVpcDeletedToMap(deletedItem vpcbetav1.VPCReferenceDeleted) (deletedMap map[string]interface{}) { + deletedMap = map[string]interface{}{} + + if deletedItem.MoreInfo != nil { + deletedMap["more_info"] = deletedItem.MoreInfo + } + + return deletedMap +} diff --git a/ibm/service/vpc/data_source_ibm_is_share_mount_targets_test.go b/ibm/service/vpc/data_source_ibm_is_share_mount_targets_test.go new file mode 100644 index 0000000000..616761a3c0 --- /dev/null +++ b/ibm/service/vpc/data_source_ibm_is_share_mount_targets_test.go @@ -0,0 +1,63 @@ +// Copyright IBM Corp. 2021 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +package vpc_test + +import ( + "fmt" + "testing" + + acc "github.com/IBM-Cloud/terraform-provider-ibm/ibm/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccIBMIsShareTargetsDataSource(t *testing.T) { + vpcName := fmt.Sprintf("tf-vpc-%d", acctest.RandIntRange(10, 100)) + targetName := fmt.Sprintf("tf-share-target-%d", acctest.RandIntRange(10, 100)) + shareName := fmt.Sprintf("tf-fs-name-%d", acctest.RandIntRange(10, 100)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMIsShareTargetsDataSourceConfigBasic(shareName, vpcName, targetName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_targets.is_share_targets", "share_targets.#"), + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_targets.is_share_targets", "share_targets.0.created_at"), + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_targets.is_share_targets", "share_targets.0.href"), + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_targets.is_share_targets", "share_targets.0.id"), + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_targets.is_share_targets", "share_targets.0.lifecycle_state"), + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_targets.is_share_targets", "share_targets.0.mount_path"), + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_targets.is_share_targets", "share_targets.0.name"), + resource.TestCheckResourceAttrSet("data.ibm_is_share_mount_targets.is_share_targets", "share_targets.0.resource_type"), + ), + }, + }, + }) +} + +func testAccCheckIBMIsShareTargetsDataSourceConfigBasic(sname, vpcName, targetName string) string { + return fmt.Sprintf(` + resource "ibm_is_share" "is_share" { + zone = "us-south-2" + size = 200 + name = "%s" + profile = "%s" + } + + resource "ibm_is_vpc" "testacc_vpc" { + name = "%s" + } + + resource "ibm_is_share_mount_target" "is_share_target" { + share = ibm_is_share.is_share.id + name = "%s" + vpc = ibm_is_vpc.testacc_vpc.id + } + + data "ibm_is_share_mount_targets" "is_share_targets" { + share = ibm_is_share_mount_target.is_share_target.share + } + `, sname, acc.ShareProfileName, vpcName, targetName) +} diff --git a/ibm/service/vpc/data_source_ibm_is_share_target.go b/ibm/service/vpc/data_source_ibm_is_share_target.go index 062d49a6ad..b4e1a79dce 100644 --- a/ibm/service/vpc/data_source_ibm_is_share_target.go +++ b/ibm/service/vpc/data_source_ibm_is_share_target.go @@ -16,7 +16,8 @@ import ( func DataSourceIbmIsShareTarget() *schema.Resource { return &schema.Resource{ - ReadContext: dataSourceIbmIsShareTargetRead, + ReadContext: dataSourceIbmIsShareTargetRead, + DeprecationMessage: "This resource is deprecated and will be removed in a future release. Please use ibm_is_share_mount_target instead", Schema: map[string]*schema.Schema{ "share": { @@ -208,7 +209,7 @@ func dataSourceIbmIsShareTargetRead(context context.Context, d *schema.ResourceD log.Printf("[DEBUG] ListShareTargetsWithContext failed %s\n%s", err, response) return diag.FromErr(err) } - for _, targetsItem := range shareTargetCollection.Targets { + for _, targetsItem := range shareTargetCollection.MountTargets { if *targetsItem.Name == share_target_name { shareTarget = &targetsItem break diff --git a/ibm/service/vpc/data_source_ibm_is_share_targets.go b/ibm/service/vpc/data_source_ibm_is_share_targets.go index a6f0524adf..8818a09e89 100644 --- a/ibm/service/vpc/data_source_ibm_is_share_targets.go +++ b/ibm/service/vpc/data_source_ibm_is_share_targets.go @@ -17,7 +17,8 @@ import ( func DataSourceIbmIsShareTargets() *schema.Resource { return &schema.Resource{ - ReadContext: dataSourceIbmIsShareTargetsRead, + ReadContext: dataSourceIbmIsShareTargetsRead, + DeprecationMessage: "This resource is deprecated and will be removed in a future release. Please use ibm_is_share_mount_targets instead", Schema: map[string]*schema.Schema{ "share": { @@ -195,8 +196,8 @@ func dataSourceIbmIsShareTargetsRead(context context.Context, d *schema.Resource d.SetId(dataSourceIbmIsShareTargetsID(d)) - if shareTargetCollection.Targets != nil { - err = d.Set("share_targets", dataSourceShareTargetCollectionFlattenTargets(shareTargetCollection.Targets)) + if shareTargetCollection.MountTargets != nil { + err = d.Set("share_targets", dataSourceShareTargetCollectionFlattenTargets(shareTargetCollection.MountTargets)) if err != nil { return diag.FromErr(fmt.Errorf("Error setting targets %s", err)) } diff --git a/ibm/service/vpc/data_source_ibm_is_shares.go b/ibm/service/vpc/data_source_ibm_is_shares.go index eef156130e..e319c6d497 100644 --- a/ibm/service/vpc/data_source_ibm_is_shares.go +++ b/ibm/service/vpc/data_source_ibm_is_shares.go @@ -486,9 +486,9 @@ func dataSourceShareCollectionSharesToMap(meta interface{}, sharesItem vpcbetav1 if sharesItem.SourceShare != nil { sharesMap["source_share"] = dataSourceShareFlattenSourceShare(*sharesItem.SourceShare) } - if sharesItem.Targets != nil { + if sharesItem.MountTargets != nil { targetsList := []map[string]interface{}{} - for _, targetsItem := range sharesItem.Targets { + for _, targetsItem := range sharesItem.MountTargets { targetsList = append(targetsList, dataSourceShareCollectionSharesTargetsToMap(targetsItem)) } sharesMap["share_targets"] = targetsList diff --git a/ibm/service/vpc/data_source_ibm_is_source_share.go b/ibm/service/vpc/data_source_ibm_is_source_share.go index ad08aaeeb8..1fe05b54b5 100644 --- a/ibm/service/vpc/data_source_ibm_is_source_share.go +++ b/ibm/service/vpc/data_source_ibm_is_source_share.go @@ -439,8 +439,8 @@ func dataSourceIbmIsSourceShareRead(context context.Context, d *schema.ResourceD return diag.FromErr(fmt.Errorf("Error setting source_share %s", err)) } } - if share.Targets != nil { - err = d.Set("share_targets", dataSourceShareFlattenTargets(share.Targets)) + if share.MountTargets != nil { + err = d.Set("share_targets", dataSourceShareFlattenTargets(share.MountTargets)) if err != nil { return diag.FromErr(fmt.Errorf("Error setting targets %s", err)) } diff --git a/ibm/service/vpc/resource_ibm_is_share.go b/ibm/service/vpc/resource_ibm_is_share.go index d9bdb405d7..fe787df2c5 100644 --- a/ibm/service/vpc/resource_ibm_is_share.go +++ b/ibm/service/vpc/resource_ibm_is_share.go @@ -581,7 +581,7 @@ func resourceIbmIsShareCreate(context context.Context, d *schema.ResourceData, m targetsItem := resourceIbmIsShareMapToShareMountTargetPrototype(target) targets = append(targets, targetsItem) } - replicaShare.Targets = targets + replicaShare.MountTargets = targets } var userTags *schema.Set @@ -638,7 +638,7 @@ func resourceIbmIsShareCreate(context context.Context, d *schema.ResourceData, m targetsItem := resourceIbmIsShareMapToShareMountTargetPrototype(value) targets = append(targets, targetsItem) } - sharePrototype.Targets = targets + sharePrototype.MountTargets = targets } if zone, ok := d.GetOk("zone"); ok { zonestr := zone.(string) @@ -768,8 +768,8 @@ func resourceIbmIsShareRead(context context.Context, d *schema.ResourceData, met return diag.FromErr(fmt.Errorf("Error setting size: %s", err)) } targets := []map[string]interface{}{} - if share.Targets != nil { - for _, targetsItem := range share.Targets { + if share.MountTargets != nil { + for _, targetsItem := range share.MountTargets { GetShareMountTargetOptions := &vpcbetav1.GetShareMountTargetOptions{} GetShareMountTargetOptions.SetShareID(d.Id()) GetShareMountTargetOptions.SetID(*targetsItem.ID) @@ -970,9 +970,9 @@ func resourceIbmIsShareDelete(context context.Context, d *schema.ResourceData, m log.Printf("[DEBUG] GetShareWithContext failed %s\n%s", err, response) return diag.FromErr(err) } - if share.Targets != nil { + if share.MountTargets != nil { if _, ok := d.GetOk("mount_targets"); ok { - for _, targetsItem := range share.Targets { + for _, targetsItem := range share.MountTargets { deleteShareMountTargetOptions := &vpcbetav1.DeleteShareMountTargetOptions{} @@ -1007,9 +1007,9 @@ func resourceIbmIsShareDelete(context context.Context, d *schema.ResourceData, m log.Printf("[DEBUG] GetShareWithContext failed %s\n%s", err, response) return diag.FromErr(err) } - if replicaShare.Targets != nil { + if replicaShare.MountTargets != nil { if _, ok := d.GetOk("replica_share.0.mount_targets"); ok { - for _, targetsItem := range replicaShare.Targets { + for _, targetsItem := range replicaShare.MountTargets { deleteShareMountTargetOptions := &vpcbetav1.DeleteShareMountTargetOptions{} @@ -1178,7 +1178,7 @@ func ShareReplicaToMap(context context.Context, vpcClient *vpcbetav1.VpcbetaV1, shareReplicaMap["replication_status_reasons"] = status_reasons targets := []map[string]interface{}{} - for _, mountTarget := range shareReplica.Targets { + for _, mountTarget := range shareReplica.MountTargets { GetShareMountTargetOptions := &vpcbetav1.GetShareMountTargetOptions{} GetShareMountTargetOptions.SetShareID(*shareReplica.ID) diff --git a/ibm/service/vpc/resource_ibm_is_share_mount_target.go b/ibm/service/vpc/resource_ibm_is_share_mount_target.go new file mode 100644 index 0000000000..5d174c13c6 --- /dev/null +++ b/ibm/service/vpc/resource_ibm_is_share_mount_target.go @@ -0,0 +1,376 @@ +// Copyright IBM Corp. 2021 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +package vpc + +import ( + "context" + "fmt" + "log" + "time" + + "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" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/IBM/vpc-beta-go-sdk/vpcbetav1" +) + +func ResourceIBMIsShareMountTarget() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceIBMIsShareMountTargetCreate, + ReadContext: resourceIBMIsShareMountTargetRead, + UpdateContext: resourceIBMIsShareMountTargetUpdate, + DeleteContext: resourceIBMIsShareMountTargetDelete, + Importer: &schema.ResourceImporter{}, + + Schema: map[string]*schema.Schema{ + "share": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The file share identifier.", + }, + "vpc": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The unique identifier of the VPC in which instances can mount the file share using this share target.This property will be removed in a future release.The `subnet` property should be used instead.", + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.InvokeValidator("ibm_is_share_mount_target", "name"), + Description: "The user-defined name for this share target. Names must be unique within the share the share target resides in. If unspecified, the name will be a hyphenated list of randomly-selected words.", + }, + "mount_target": { + Type: schema.TypeString, + Computed: true, + Description: "The unique identifier of this target", + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: "The date and time that the share target was created.", + }, + "href": { + Type: schema.TypeString, + Computed: true, + Description: "The URL for this share target.", + }, + "lifecycle_state": { + Type: schema.TypeString, + Computed: true, + Description: "The lifecycle state of the mount target.", + }, + "mount_path": { + Type: schema.TypeString, + Computed: true, + Description: "The mount path for the share.The IP addresses used in the mount path are currently within the IBM services IP range, but are expected to change to be within one of the VPC's subnets in the future.", + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of resource referenced.", + }, + }, + } +} + +func ResourceIBMIsShareMountTargetValidator() *validate.ResourceValidator { + validateSchema := make([]validate.ValidateSchema, 1) + validateSchema = append(validateSchema, + validate.ValidateSchema{ + Identifier: "name", + ValidateFunctionIdentifier: validate.ValidateRegexpLen, + Type: validate.TypeString, + Optional: true, + Regexp: `^([a-z]|[a-z][-a-z0-9]*[a-z0-9]|[0-9][-a-z0-9]*([a-z]|[-a-z][-a-z0-9]*[a-z0-9]))$`, + MinValueLength: 1, + MaxValueLength: 63, + }, + ) + + resourceValidator := validate.ResourceValidator{ResourceName: "ibm_is_share_mount_target", Schema: validateSchema} + return &resourceValidator +} + +func isWaitForOldTargetDelete(context context.Context, vpcClient *vpcbetav1.VpcbetaV1, d *schema.ResourceData, shareid, targetid string) { + + shareTargetOptions := &vpcbetav1.GetShareMountTargetOptions{} + + shareTargetOptions.SetShareID(shareid) + shareTargetOptions.SetID(targetid) + for i := 0; i < 6; i++ { + target, _, err := vpcClient.GetShareMountTargetWithContext(context, shareTargetOptions) + if err != nil { + return + } + if target != nil && *target.LifecycleState != "deleting" { + time.Sleep(10 * time.Second) + } else { + break + } + } + + return +} + +func resourceIBMIsShareMountTargetCreate(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + vpcClient, err := meta.(conns.ClientSession).VpcV1BetaAPI() + if err != nil { + return diag.FromErr(err) + } + + //Temporary code to fix concurrent mount target issues. + listShareMountTargetOptions := &vpcbetav1.ListShareMountTargetsOptions{} + shareId := d.Get("share").(string) + vpcId := d.Get("vpc").(string) + listShareMountTargetOptions.SetShareID(shareId) + + shareTargets, response, err := vpcClient.ListShareMountTargetsWithContext(context, listShareMountTargetOptions) + if err != nil || shareTargets == nil { + if response != nil && response.StatusCode == 404 { + d.SetId("") + return nil + } + log.Printf("[DEBUG] ListShareMountTargetsWithContext failed %s\n%s", err, response) + return diag.FromErr(err) + } + for _, mountTargets := range shareTargets.MountTargets { + if mountTargets.VPC != nil && *mountTargets.VPC.ID == vpcId { + isWaitForOldTargetDelete(context, vpcClient, d, shareId, *mountTargets.ID) + if *mountTargets.LifecycleState == "deleting" { + _, err = isWaitForTargetDelete(context, vpcClient, d, shareId, *mountTargets.ID) + if err != nil { + return diag.FromErr(err) + } + } + } + } + + createShareMountTargetOptions := &vpcbetav1.CreateShareMountTargetOptions{} + + createShareMountTargetOptions.SetShareID(d.Get("share").(string)) + vpcid := d.Get("vpc").(string) + vpc := &vpcbetav1.VPCIdentity{ + ID: &vpcid, + } + createShareMountTargetOptions.SetVPC(vpc) + if _, ok := d.GetOk("name"); ok { + createShareMountTargetOptions.SetName(d.Get("name").(string)) + } + + shareTarget, response, err := vpcClient.CreateShareMountTargetWithContext(context, createShareMountTargetOptions) + if err != nil { + log.Printf("[DEBUG] CreateShareMountTargetWithContext failed %s\n%s", err, response) + return diag.FromErr(err) + } + d.SetId(fmt.Sprintf("%s/%s", *createShareMountTargetOptions.ShareID, *shareTarget.ID)) + _, err = WaitForTargetAvailable(context, vpcClient, *createShareMountTargetOptions.ShareID, *shareTarget.ID, d, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return diag.FromErr(err) + } + + d.Set("mount_target", *shareTarget.ID) + return resourceIBMIsShareMountTargetRead(context, d, meta) +} + +func resourceIBMIsShareMountTargetRead(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + vpcClient, err := meta.(conns.ClientSession).VpcV1BetaAPI() + if err != nil { + return diag.FromErr(err) + } + + getShareMountTargetOptions := &vpcbetav1.GetShareMountTargetOptions{} + + parts, err := flex.IdParts(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + getShareMountTargetOptions.SetShareID(parts[0]) + getShareMountTargetOptions.SetID(parts[1]) + + shareTarget, response, err := vpcClient.GetShareMountTargetWithContext(context, getShareMountTargetOptions) + if err != nil || shareTarget == nil { + if response != nil && response.StatusCode == 404 { + d.SetId("") + return nil + } + log.Printf("[DEBUG] GetShareMountTargetWithContext failed %s\n%s", err, response) + return diag.FromErr(err) + } + + d.Set("mount_target", *shareTarget.ID) + + if err = d.Set("vpc", *shareTarget.VPC.ID); err != nil { + return diag.FromErr(fmt.Errorf("Error setting name: %s", err)) + } + + if err = d.Set("name", *shareTarget.Name); err != nil { + return diag.FromErr(fmt.Errorf("Error setting name: %s", err)) + } + + if err = d.Set("created_at", shareTarget.CreatedAt.String()); err != nil { + return diag.FromErr(fmt.Errorf("Error setting created_at: %s", err)) + } + if err = d.Set("href", shareTarget.Href); err != nil { + return diag.FromErr(fmt.Errorf("Error setting href: %s", err)) + } + if err = d.Set("lifecycle_state", shareTarget.LifecycleState); err != nil { + return diag.FromErr(fmt.Errorf("Error setting lifecycle_state: %s", err)) + } + if err = d.Set("mount_path", shareTarget.MountPath); err != nil { + return diag.FromErr(fmt.Errorf("Error setting mount_path: %s", err)) + } + if err = d.Set("resource_type", shareTarget.ResourceType); err != nil { + return diag.FromErr(fmt.Errorf("Error setting resource_type: %s", err)) + } + + return nil +} + +func resourceIBMIsShareMountTargetUpdate(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + vpcClient, err := meta.(conns.ClientSession).VpcV1BetaAPI() + if err != nil { + return diag.FromErr(err) + } + + updateShareMountTargetOptions := &vpcbetav1.UpdateShareMountTargetOptions{} + + parts, err := flex.IdParts(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + updateShareMountTargetOptions.SetShareID(parts[0]) + updateShareMountTargetOptions.SetID(parts[1]) + + hasChange := false + + shareTargetPatchModel := &vpcbetav1.ShareMountTargetPatch{} + + if d.HasChange("name") { + name := d.Get("name").(string) + shareTargetPatchModel.Name = &name + hasChange = true + } + + if hasChange { + shareTargetPatch, err := shareTargetPatchModel.AsPatch() + if err != nil { + log.Printf("[DEBUG] ShareMountTargetPatch AsPatch failed %s", err) + return diag.FromErr(err) + } + updateShareMountTargetOptions.SetShareMountTargetPatch(shareTargetPatch) + _, response, err := vpcClient.UpdateShareMountTargetWithContext(context, updateShareMountTargetOptions) + if err != nil { + log.Printf("[DEBUG] UpdateShareMountTargetWithContext failed %s\n%s", err, response) + return diag.FromErr(err) + } + } + + return resourceIBMIsShareMountTargetRead(context, d, meta) +} + +func resourceIBMIsShareMountTargetDelete(context context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + vpcClient, err := meta.(conns.ClientSession).VpcV1BetaAPI() + if err != nil { + return diag.FromErr(err) + } + + deleteShareMountTargetOptions := &vpcbetav1.DeleteShareMountTargetOptions{} + + parts, err := flex.IdParts(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + deleteShareMountTargetOptions.SetShareID(parts[0]) + deleteShareMountTargetOptions.SetID(parts[1]) + + _, response, err := vpcClient.DeleteShareMountTargetWithContext(context, deleteShareMountTargetOptions) + if err != nil { + log.Printf("[DEBUG] DeleteShareMountTargetWithContext failed %s\n%s", err, response) + return diag.FromErr(err) + } + _, err = isWaitForMountTargetDelete(context, vpcClient, d, parts[0], parts[1]) + if err != nil { + return diag.FromErr(err) + } + + d.SetId("") + + return nil +} + +func WaitForMountTargetAvailable(context context.Context, vpcClient *vpcbetav1.VpcbetaV1, shareid, targetid string, d *schema.ResourceData, timeout time.Duration) (interface{}, error) { + log.Printf("Waiting for target (%s) to be available.", targetid) + + stateConf := &resource.StateChangeConf{ + Pending: []string{"updating", "pending", "waiting"}, + Target: []string{"stable", "failed"}, + Refresh: mountTargetRefresh(context, vpcClient, shareid, targetid, d), + Timeout: timeout, + Delay: 10 * time.Second, + MinTimeout: 10 * time.Second, + } + + return stateConf.WaitForState() +} + +func mountTargetRefresh(context context.Context, vpcClient *vpcbetav1.VpcbetaV1, shareid, targetid string, d *schema.ResourceData) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + shareTargetOptions := &vpcbetav1.GetShareMountTargetOptions{} + + shareTargetOptions.SetShareID(shareid) + shareTargetOptions.SetID(targetid) + + target, response, err := vpcClient.GetShareMountTargetWithContext(context, shareTargetOptions) + if err != nil { + return nil, "", fmt.Errorf("Error Getting target: %s\n%s", err, response) + } + d.Set("lifecycle_state", *target.LifecycleState) + if *target.LifecycleState == "stable" || *target.LifecycleState == "failed" { + + return target, *target.LifecycleState, nil + + } + return target, "pending", nil + } +} + +func isWaitForMountTargetDelete(context context.Context, vpcClient *vpcbetav1.VpcbetaV1, d *schema.ResourceData, shareid, targetid string) (interface{}, error) { + + stateConf := &resource.StateChangeConf{ + Pending: []string{"deleting", "stable"}, + Target: []string{"done"}, + Refresh: func() (interface{}, string, error) { + shareTargetOptions := &vpcbetav1.GetShareMountTargetOptions{} + + shareTargetOptions.SetShareID(shareid) + shareTargetOptions.SetID(targetid) + + target, response, err := vpcClient.GetShareMountTargetWithContext(context, shareTargetOptions) + if err != nil { + if response != nil && response.StatusCode == 404 { + return target, "done", nil + } + return nil, "", fmt.Errorf("Error Getting Target: %s\n%s", err, response) + } + if *target.LifecycleState == isInstanceFailed { + return target, *target.LifecycleState, fmt.Errorf("The target %s failed to delete: %v", targetid, err) + } + return target, "deleting", nil + }, + Timeout: d.Timeout(schema.TimeoutDelete), + Delay: 10 * time.Second, + MinTimeout: 10 * time.Second, + } + + return stateConf.WaitForState() +} diff --git a/ibm/service/vpc/resource_ibm_is_share_mount_target_test.go b/ibm/service/vpc/resource_ibm_is_share_mount_target_test.go new file mode 100644 index 0000000000..2646b89a55 --- /dev/null +++ b/ibm/service/vpc/resource_ibm_is_share_mount_target_test.go @@ -0,0 +1,133 @@ +// Copyright IBM Corp. 2021 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +package vpc_test + +import ( + "fmt" + "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/vpc-beta-go-sdk/vpcbetav1" + "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" +) + +func TestAccIbmIsShareMountTarget(t *testing.T) { + var conf vpcbetav1.ShareMountTarget + vpcname := fmt.Sprintf("tf-vpc-name-%d", acctest.RandIntRange(10, 100)) + targetName := fmt.Sprintf("tf-target-%d", acctest.RandIntRange(10, 100)) + targetNameUpdate := fmt.Sprintf("tf-target-%d", acctest.RandIntRange(10, 100)) + sname := fmt.Sprintf("tf-fs-name-%d", acctest.RandIntRange(10, 100)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIbmIsShareMountTargetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIbmIsShareTargetConfig(vpcname, sname, targetName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIbmIsShareMountTargetExists("ibm_is_share_mount_target.is_share_target", conf), + resource.TestCheckResourceAttr("ibm_is_share_mount_target.is_share_target", "name", targetName), + ), + }, + { + Config: testAccCheckIbmIsShareTargetConfig(vpcname, sname, targetNameUpdate), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("ibm_is_share_mount_target.is_share_target", "name", targetNameUpdate), + ), + }, + }, + }) +} + +func testAccCheckIbmIsShareTargetConfig(vpcName, sname, targetName string) string { + return fmt.Sprintf(` + data "ibm_resource_group" "group" { + is_default = "true" + } + resource "ibm_is_share" "is_share" { + zone = "us-south-2" + size = 200 + name = "%s" + profile = "%s" + } + resource "ibm_is_vpc" "testacc_vpc" { + name = "%s" + } + resource "ibm_is_share_mount_target" "is_share_target" { + share = ibm_is_share.is_share.id + vpc = ibm_is_vpc.testacc_vpc.id + name = "%s" + } + `, sname, acc.ShareProfileName, vpcName, targetName) +} + +func testAccCheckIbmIsShareMountTargetExists(n string, obj vpcbetav1.ShareMountTarget) resource.TestCheckFunc { + + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + vpcClient, err := acc.TestAccProvider.Meta().(conns.ClientSession).VpcV1BetaAPI() + if err != nil { + return err + } + + getShareTargetOptions := &vpcbetav1.GetShareMountTargetOptions{} + + parts, err := flex.IdParts(rs.Primary.ID) + if err != nil { + return err + } + + getShareTargetOptions.SetShareID(parts[0]) + getShareTargetOptions.SetID(parts[1]) + + shareTarget, _, err := vpcClient.GetShareMountTarget(getShareTargetOptions) + if err != nil { + return err + } + + obj = *shareTarget + return nil + } +} + +func testAccCheckIbmIsShareMountTargetDestroy(s *terraform.State) error { + vpcClient, err := acc.TestAccProvider.Meta().(conns.ClientSession).VpcV1BetaAPI() + if err != nil { + return err + } + for _, rs := range s.RootModule().Resources { + if rs.Type != "ibm_is_share_mount_target" { + continue + } + + getShareTargetOptions := &vpcbetav1.GetShareMountTargetOptions{} + + parts, err := flex.IdParts(rs.Primary.ID) + if err != nil { + return err + } + + getShareTargetOptions.SetShareID(parts[0]) + getShareTargetOptions.SetID(parts[1]) + + // Try to find the key + _, response, err := vpcClient.GetShareMountTarget(getShareTargetOptions) + + if err == nil { + return fmt.Errorf("ShareTarget still exists: %s", rs.Primary.ID) + } else if response.StatusCode != 404 { + return fmt.Errorf("Error checking for ShareTarget (%s) has been destroyed: %s", rs.Primary.ID, err) + } + } + + return nil +} diff --git a/ibm/service/vpc/resource_ibm_is_share_target.go b/ibm/service/vpc/resource_ibm_is_share_target.go index d49d787779..2e7dafacbf 100644 --- a/ibm/service/vpc/resource_ibm_is_share_target.go +++ b/ibm/service/vpc/resource_ibm_is_share_target.go @@ -21,11 +21,12 @@ import ( func ResourceIbmIsShareMountTarget() *schema.Resource { return &schema.Resource{ - CreateContext: resourceIbmIsShareMountTargetCreate, - ReadContext: resourceIbmIsShareMountTargetRead, - UpdateContext: resourceIbmIsShareMountTargetUpdate, - DeleteContext: resourceIbmIsShareMountTargetDelete, - Importer: &schema.ResourceImporter{}, + CreateContext: resourceIbmIsShareMountTargetCreate, + ReadContext: resourceIbmIsShareMountTargetRead, + UpdateContext: resourceIbmIsShareMountTargetUpdate, + DeleteContext: resourceIbmIsShareMountTargetDelete, + DeprecationMessage: "This resource is deprecated and will be removed in a future release. Please use ibm_is_share_mount_target instead", + Importer: &schema.ResourceImporter{}, Schema: map[string]*schema.Schema{ "share": { diff --git a/website/docs/d/is_share_mount_target.html.markdown b/website/docs/d/is_share_mount_target.html.markdown new file mode 100644 index 0000000000..f1f7f323a7 --- /dev/null +++ b/website/docs/d/is_share_mount_target.html.markdown @@ -0,0 +1,72 @@ +--- +layout: "ibm" +page_title: "IBM : is_share_mount_target" +description: |- + Get information about ShareMountTarget +subcategory: "VPC infrastructure" +--- + +# ibm\_is_share_mount_target + +Provides a read-only data source for ShareMountTarget. You can then reference the fields of the data source in other resources within the same configuration using interpolation syntax. + + +~> **NOTE** +IBM Cloud® File Storage for VPC is available for customers with special approval. Contact your IBM Sales representative if you are interested in getting access. + +~> **NOTE** +This is a Beta feature and it is subject to change in the GA release + + + +## Example Usage + +```hcl +resource "ibm_is_vpc" "example" { + name = "example-vpc" +} +resource "ibm_is_share" "example" { + name = "example-share" + size = 200 + profile = "tier-3iops" + zone = "us-south-2" +} + +resource "ibm_is_share_mount_target" "example" { + share = ibm_is_share.is_share.id + vpc = ibm_is_vpc.example.id + name = "example-share-target" +} + +data "ibm_is_share_mount_target" "example" { + share = ibm_is_share.example.id + mount_target = ibm_is_share_mount_target.example.mount_target +} +``` + +## Argument Reference + +The following arguments are supported: + +- `share` - (Required, string) The file share identifier. +- `mount_target` - (Required, string) The share target identifier. + +## Attribute Reference + +The following attributes are exported: + +- `created_at` - The date and time that the share target was created. +- `href` - The URL for this share target. +- `lifecycle_state` - The lifecycle state of the mount target. +- `mount_path` - The mount path for the share.The IP addresses used in the mount path are currently within the IBM services IP range, but are expected to change to be within one of the VPC's subnets in the future. +- `name` - The user-defined name for this share target. +- `resource_type` - The type of resource referenced. +- `vpc` - The VPC to which this share target is allowing to mount the file share. Nested `vpc` blocks have the following structure: + - `crn` - The CRN for this VPC. + - `deleted` - If present, this property indicates the referenced resource has been deleted and providessome supplementary information. Nested `deleted` blocks have the following structure: + - `more_info` - Link to documentation about deleted resources. + - `href` - The URL for this VPC. + - `id` - The unique identifier for this VPC. + - `name` - The unique user-defined name for this VPC. + - `resource_type` - The resource type. + diff --git a/website/docs/d/is_share_mount_targets.html.markdown b/website/docs/d/is_share_mount_targets.html.markdown new file mode 100644 index 0000000000..739611cf1a --- /dev/null +++ b/website/docs/d/is_share_mount_targets.html.markdown @@ -0,0 +1,67 @@ +--- +layout: "ibm" +page_title: "IBM : is_share_mount_targets" +description: |- + Get information about ShareMountTargetCollection +subcategory: "VPC infrastructure" +--- + +# ibm\_is_share_mount_targets + +Provides a read-only data source for ShareMountTargetCollection. You can then reference the fields of the data source in other resources within the same configuration using interpolation syntax. + + +~> **NOTE** +IBM Cloud® File Storage for VPC is available for customers with special approval. Contact your IBM Sales representative if you are interested in getting access. + +~> **NOTE** +This is a Beta feature and it is subject to change in the GA release + + + +## Example Usage + +```hcl +resource "ibm_is_vpc" "example" { + name = "example-vpc" +} +resource "ibm_is_share" "example" { + name = "example-share" + size = 200 + profile = "tier-3iops" + zone = "us-south-2" +} + +data "ibm_is_share_mount_targets" "example" { + share = ibm_is_share.example.id +} +``` + +## Argument Reference + +The following arguments are supported: + +- `share` - (Required, string) The file share identifier. + +## Attribute Reference + +The following attributes are exported: + +- `id` - The unique identifier of the ShareTargetCollection. +- `mount_targets` - Collection of share targets. Nested `targets` blocks have the following structure: + - `created_at` - The date and time that the share target was created. + - `href` - The URL for this share target. + - `id` - The unique identifier for this share target. + - `lifecycle_state` - The lifecycle state of the mount target. + - `mount_path` - The mount path for the share.The IP addresses used in the mount path are currently within the IBM services IP range, but are expected to change to be within one of the VPC's subnets in the future. + - `name` - The user-defined name for this share target. + - `resource_type` - The type of resource referenced. + - `vpc` - The VPC to which this share target is allowing to mount the file share. Nested `vpc` blocks have the following structure: + - `crn` - The CRN for this VPC. + - `deleted` - If present, this property indicates the referenced resource has been deleted and providessome supplementary information. Nested `deleted` blocks have the following structure: + - `more_info` - Link to documentation about deleted resources. + - `href` - The URL for this VPC. + - `id` - The unique identifier for this VPC. + - `name` - The unique user-defined name for this VPC. + - `resource_type` - The resource type. + diff --git a/website/docs/d/is_share_target.html.markdown b/website/docs/d/is_share_target.html.markdown index b56b05e983..80ba137552 100644 --- a/website/docs/d/is_share_target.html.markdown +++ b/website/docs/d/is_share_target.html.markdown @@ -17,6 +17,8 @@ IBM Cloud® File Storage for VPC is available for customers with special approva ~> **NOTE** This is a Beta feature and it is subject to change in the GA release +~> **NOTE** +This data source is being deprecated. Please use `ibm_is_share_mount_target` instead ## Example Usage diff --git a/website/docs/d/is_share_targets.html.markdown b/website/docs/d/is_share_targets.html.markdown index 204a0fae66..55bd35df69 100644 --- a/website/docs/d/is_share_targets.html.markdown +++ b/website/docs/d/is_share_targets.html.markdown @@ -17,7 +17,8 @@ IBM Cloud® File Storage for VPC is available for customers with special approva ~> **NOTE** This is a Beta feature and it is subject to change in the GA release - +~> **NOTE** +This data source is being deprecated. Please use `ibm_is_share_mount_targets` instead ## Example Usage diff --git a/website/docs/r/is_share_mount_target.html.markdown b/website/docs/r/is_share_mount_target.html.markdown new file mode 100644 index 0000000000..cf67dded50 --- /dev/null +++ b/website/docs/r/is_share_mount_target.html.markdown @@ -0,0 +1,77 @@ +--- +layout: "ibm" +page_title: "IBM : is_share_mount_target" +description: |- + Manages ShareTarget. +subcategory: "VPC infrastructure" +--- + + +# ibm\_is_share_mount_target + +Provides a resource for ShareMountTarget. This allows ShareTarget to be created, updated and deleted. + + +~> **NOTE** +IBM Cloud® File Storage for VPC is available for customers with special approval. Contact your IBM Sales representative if you are interested in getting access. + +~> **NOTE** +This is a Beta feature and it is subject to change in the GA release + +## Example Usage + +```hcl +resource "ibm_is_vpc" "vpc" { + name = "my-vpc" +} + +resource "ibm_is_share" "is_share" { + name = "my-share" + size = 200 + profile = "tier-3iops" + zone = "us-south-2" +} + +resource "ibm_is_share_mount_target" "is_share_target" { + share = ibm_is_share.is_share.id + vpc = ibm_is_vpc.vpc.id + name = "my-share-target" +}` +``` + +## Argument Reference + +The following arguments are supported: + +- `share` - (Required, String) The file share identifier. +- `vpc` - (Required, String) The VPC in which instances can mount the file share using this share target.This property will be removed in a future release.The `subnet` property should be used instead. +- `name` - (Required, String) The user-defined name for this share target. Names must be unique within the share the share target resides in. If unspecified, the name will be a hyphenated list of randomly-selected words. +## Attribute Reference + +The following attributes are exported: + + +- `mount_target` - The unique identifier of the share target +- `created_at` - The date and time that the share target was created. +- `href` - The URL for this share target. +- `id` - The unique identifier of the ShareTarget. The id is composed of \/\ +- `lifecycle_state` - The lifecycle state of the mount target. +- `mount_path` - The mount path for the share.The IP addresses used in the mount path are currently within the IBM services IP range, but are expected to change to be within one of the VPC's subnets in the future. +- `resource_type` - The type of resource referenced. + + +## Import + +The `ibm_is_share_target` can be imported using ID. + +**Syntax** + +``` +$ terraform import ibm_is_share_target.example `\/\` +``` + +**Example** + +``` +$ terraform import ibm_is_share_target.example d7bec597-4726-451f-8a63-e62e6f19c32c/d7bec597-4726-451f-8a63-e62e6f19c32c +``` \ No newline at end of file diff --git a/website/docs/r/is_share_target.html.markdown b/website/docs/r/is_share_target.html.markdown index 1c07a82ab6..4c7d2c7558 100644 --- a/website/docs/r/is_share_target.html.markdown +++ b/website/docs/r/is_share_target.html.markdown @@ -18,6 +18,9 @@ IBM Cloud® File Storage for VPC is available for customers with special approva ~> **NOTE** This is a Beta feature and it is subject to change in the GA release +~> **NOTE** +This resource is being deprecated. Please use `ibm_is_share_mount_target` instead + ## Example Usage ```hcl