From 3141fd733c64006f99dc45fcadfcc72d4a15dd89 Mon Sep 17 00:00:00 2001 From: SunithaGudisagar Date: Mon, 20 May 2024 12:59:49 +0530 Subject: [PATCH 1/5] Snapshots: Cross-account snapshot restore - CRN support --- ibm/service/vpc/resource_ibm_is_instance.go | 20 +++++++++ ...ource_ibm_is_instance_volume_attachment.go | 20 +++++++++ ibm/service/vpc/resource_ibm_is_volume.go | 41 +++++++++++-------- website/docs/r/is_instance.html.markdown | 2 +- ...s_instance_volume_attachment.html.markdown | 2 +- website/docs/r/is_volume.html.markdown | 2 +- 6 files changed, 68 insertions(+), 19 deletions(-) diff --git a/ibm/service/vpc/resource_ibm_is_instance.go b/ibm/service/vpc/resource_ibm_is_instance.go index 9d7deef928c..5ac3350541d 100644 --- a/ibm/service/vpc/resource_ibm_is_instance.go +++ b/ibm/service/vpc/resource_ibm_is_instance.go @@ -3066,6 +3066,13 @@ func instanceCreateBySnapshot(d *schema.ResourceData, meta interface{}, profile, snapshotId, ok := bootvol[isInstanceVolumeSnapshot] snapshotIdStr := snapshotId.(string) if snapshotIdStr != "" && ok { + isCRN, sourceSnapshotId, err := ValidateCRN(snapshotIdStr) + if err != nil { + return utils.FailGotError(err, cmd.UI) + } + if isCRN { + snapshotIdStr = sourceSnapshotId + } volTemplate.SourceSnapshot = &vpcv1.SnapshotIdentity{ ID: &snapshotIdStr, } @@ -6365,3 +6372,16 @@ func containsNacId(s []string, e string) bool { } return false } + +func ValidateCRN(crn string) (bool, id, error) { + validInput := strings.Contains(crn, "crn:") + if validInput { + validateValue := strings.Split(crn, ":") + if validateValue[0] == "crn" { + return true, validateValue[len(validateValue)-1], nil + } else { + return false, 0, fmt.Errorf("Invalid CRN. Please pass correct CRN.") + } + } + return false, 0, nil +} diff --git a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go index 4c1cb7ed416..01d52a3e208 100644 --- a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go +++ b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go @@ -314,6 +314,13 @@ func instanceVolAttachmentCreate(d *schema.ResourceData, meta interface{}, insta volSnapshotStr := "" if volSnapshot, ok := d.GetOk(isInstanceVolumeSnapshot); ok { volSnapshotStr = volSnapshot.(string) + isCRN, sourceSnapshotId, err := ValidateCRN(volSnapshotStr) + if err != nil { + return utils.FailGotError(err, cmd.UI) + } + if isCRN { + volSnapshotStr = sourceSnapshotId + } volProtoVol.SourceSnapshot = &vpcv1.SnapshotIdentity{ ID: &volSnapshotStr, } @@ -808,3 +815,16 @@ func parseVolAttTerraformID(s string) (string, string, error) { } return segments[0], segments[1], nil } + +func ValidateCRN(crn string) (bool, id, error) { + validInput := strings.Contains(crn, "crn:") + if validInput { + validateValue := strings.Split(crn, ":") + if validateValue[0] == "crn" { + return true, validateValue[len(validateValue)-1], nil + } else { + return false, 0, fmt.Errorf("Invalid CRN. Please pass correct CRN.") + } + } + return false, 0, nil +} diff --git a/ibm/service/vpc/resource_ibm_is_volume.go b/ibm/service/vpc/resource_ibm_is_volume.go index ba2507f4305..b7ddba159cd 100644 --- a/ibm/service/vpc/resource_ibm_is_volume.go +++ b/ibm/service/vpc/resource_ibm_is_volume.go @@ -127,12 +127,11 @@ func ResourceIBMISVolume() *schema.Resource { Description: "Volume capacity value", }, isVolumeSourceSnapshot: { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Computed: true, - ValidateFunc: validate.InvokeValidator("ibm_is_volume", isVolumeSourceSnapshot), - Description: "The unique identifier for this snapshot", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + Description: "The unique identifier for this snapshot", }, isVolumeResourceGroup: { Type: schema.TypeString, @@ -364,15 +363,6 @@ func ResourceIBMISVolumeValidator() *validate.ResourceValidator { Type: validate.TypeInt, MinValue: "10", MaxValue: "16000"}) - validateSchema = append(validateSchema, - validate.ValidateSchema{ - Identifier: isVolumeSourceSnapshot, - ValidateFunctionIdentifier: validate.ValidateRegexpLen, - Type: validate.TypeString, - Optional: true, - Regexp: `^[-0-9a-z_]+$`, - MinValueLength: 1, - MaxValueLength: 64}) validateSchema = append(validateSchema, validate.ValidateSchema{ Identifier: isVolumeIops, @@ -413,7 +403,6 @@ func volCreate(d *schema.ResourceData, meta interface{}, volName, profile, zone if err != nil { return err } - log.Println("I AM INSIDE func volCreate(d *schema.ResourceData, meta interface{}, volName, profile, zone string)") options := &vpcv1.CreateVolumeOptions{ VolumePrototype: &vpcv1.VolumePrototype{ Name: &volName, @@ -430,6 +419,13 @@ func volCreate(d *schema.ResourceData, meta interface{}, volName, profile, zone var volCapacity int64 if sourceSnapsht, ok := d.GetOk(isVolumeSourceSnapshot); ok { sourceSnapshot := sourceSnapsht.(string) + isCRN, sourceSnapshotId, err := ValidateCRN(sourceSnapshot) + if err != nil { + return utils.FailGotError(err, cmd.UI) + } + if isCRN { + sourceSnapshot = sourceSnapshotId + } snapshotIdentity := &vpcv1.SnapshotIdentity{ ID: &sourceSnapshot, } @@ -1040,3 +1036,16 @@ func deleteAllSnapshots(sess *vpcv1.VpcV1, id string) error { } return nil } + +func ValidateCRN(crn string) (bool, id, error) { + validInput := strings.Contains(crn, "crn:") + if validInput { + validateValue := strings.Split(crn, ":") + if validateValue[0] == "crn" { + return true, validateValue[len(validateValue)-1], nil + } else { + return false, 0, fmt.Errorf("Invalid CRN. Please pass correct CRN.") + } + } + return false, 0, nil +} diff --git a/website/docs/r/is_instance.html.markdown b/website/docs/r/is_instance.html.markdown index c182fe36f7f..230354dc6cf 100644 --- a/website/docs/r/is_instance.html.markdown +++ b/website/docs/r/is_instance.html.markdown @@ -520,7 +520,7 @@ Review the argument references that you can specify for your resource. ~> **NOTE:** Supports only expansion on update (must be attached to a running instance and must not be less than the current volume size) - - `snapshot` - (Optional, Forces new resource, String) The snapshot id of the volume to be used for creating boot volume attachment + - `snapshot` - (Optional, Forces new resource, String) The snapshot id or crn of the volume to be used for creating boot volume attachment ~> **Note:** `snapshot` conflicts with `image` id, `instance_template` , `catalog_offering` and `boot_volume.volume_id` diff --git a/website/docs/r/is_instance_volume_attachment.html.markdown b/website/docs/r/is_instance_volume_attachment.html.markdown index ba4601a9a7b..b2b71786b24 100644 --- a/website/docs/r/is_instance_volume_attachment.html.markdown +++ b/website/docs/r/is_instance_volume_attachment.html.markdown @@ -193,7 +193,7 @@ Review the argument references that you can specify for your resource. **•** Tiered profiles [`general-purpose`, `5iops-tier`, `10iops-tier`] can be upgraded and downgraded into each other.
**•** Can be updated only if volume is attached to an running virtual server instance.
**•** Stopped instances will be started on update of volume.
-- `snapshot` - (Optional, String) The unique identifier for this snapshot from which to clone the new volume. +- `snapshot` - (Optional, String) The unique identifier or CRN for this snapshot from which to clone the new volume. ~> **NOTE** **•** one of `capacity` or `snapshot` must be present for volume creation.
diff --git a/website/docs/r/is_volume.html.markdown b/website/docs/r/is_volume.html.markdown index 372319ae2d8..ed0ddcd2adf 100644 --- a/website/docs/r/is_volume.html.markdown +++ b/website/docs/r/is_volume.html.markdown @@ -103,7 +103,7 @@ Review the argument references that you can specify for your resource. ~> **NOTE:** tiered profiles [`general-purpose`, `5iops-tier`, `10iops-tier`] can be upgraded and downgraded into each other if volume is attached to an running virtual server instance. Stopped instances will be started on update of volume. - `resource_group` - (Optional, Forces new resource, String) The resource group ID for this volume. - `resource_controller_url` - (Optional, Forces new resource, String) The URL of the IBM Cloud dashboard that can be used to explore and view details about this instance. -- `source_snapshot` - The ID of snapshot from which to clone the volume. +- `source_snapshot` - The ID or CRN of snapshot from which to clone the volume. - `tags`- (Optional, Array of Strings) A list of user tags that you want to add to your volume. (https://cloud.ibm.com/apidocs/tagging#types-of-tags) - `zone` - (Required, Forces new resource, String) The location of the volume. From a2b6f703ce2afef844d4b8e92817df39e92c30b8 Mon Sep 17 00:00:00 2001 From: SunithaGudisagar Date: Mon, 20 May 2024 13:11:16 +0530 Subject: [PATCH 2/5] Utils Error Resolved --- ibm/service/vpc/resource_ibm_is_instance.go | 2 +- ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go | 2 +- ibm/service/vpc/resource_ibm_is_volume.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ibm/service/vpc/resource_ibm_is_instance.go b/ibm/service/vpc/resource_ibm_is_instance.go index 5ac3350541d..03d2f216ce9 100644 --- a/ibm/service/vpc/resource_ibm_is_instance.go +++ b/ibm/service/vpc/resource_ibm_is_instance.go @@ -3068,7 +3068,7 @@ func instanceCreateBySnapshot(d *schema.ResourceData, meta interface{}, profile, if snapshotIdStr != "" && ok { isCRN, sourceSnapshotId, err := ValidateCRN(snapshotIdStr) if err != nil { - return utils.FailGotError(err, cmd.UI) + return fmt.Errorf("[ERROR] Error invalid CRN: %q", err) } if isCRN { snapshotIdStr = sourceSnapshotId diff --git a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go index 01d52a3e208..d5aac2939d7 100644 --- a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go +++ b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go @@ -316,7 +316,7 @@ func instanceVolAttachmentCreate(d *schema.ResourceData, meta interface{}, insta volSnapshotStr = volSnapshot.(string) isCRN, sourceSnapshotId, err := ValidateCRN(volSnapshotStr) if err != nil { - return utils.FailGotError(err, cmd.UI) + return fmt.Errorf("[ERROR] Error invalid CRN: %q", err) } if isCRN { volSnapshotStr = sourceSnapshotId diff --git a/ibm/service/vpc/resource_ibm_is_volume.go b/ibm/service/vpc/resource_ibm_is_volume.go index b7ddba159cd..f5ae21be718 100644 --- a/ibm/service/vpc/resource_ibm_is_volume.go +++ b/ibm/service/vpc/resource_ibm_is_volume.go @@ -421,7 +421,7 @@ func volCreate(d *schema.ResourceData, meta interface{}, volName, profile, zone sourceSnapshot := sourceSnapsht.(string) isCRN, sourceSnapshotId, err := ValidateCRN(sourceSnapshot) if err != nil { - return utils.FailGotError(err, cmd.UI) + return fmt.Errorf("[ERROR] Error invalid CRN: %q", err) } if isCRN { sourceSnapshot = sourceSnapshotId From 58fad87518829703d36dc152654bbbdd663cb0ed Mon Sep 17 00:00:00 2001 From: SunithaGudisagar Date: Mon, 20 May 2024 23:10:02 +0530 Subject: [PATCH 3/5] Error Fixed --- ibm/service/vpc/resource_ibm_is_instance.go | 13 ------------- .../resource_ibm_is_instance_volume_attachment.go | 6 +++--- ibm/service/vpc/resource_ibm_is_volume.go | 13 ------------- 3 files changed, 3 insertions(+), 29 deletions(-) diff --git a/ibm/service/vpc/resource_ibm_is_instance.go b/ibm/service/vpc/resource_ibm_is_instance.go index 03d2f216ce9..233dc33e8fb 100644 --- a/ibm/service/vpc/resource_ibm_is_instance.go +++ b/ibm/service/vpc/resource_ibm_is_instance.go @@ -6372,16 +6372,3 @@ func containsNacId(s []string, e string) bool { } return false } - -func ValidateCRN(crn string) (bool, id, error) { - validInput := strings.Contains(crn, "crn:") - if validInput { - validateValue := strings.Split(crn, ":") - if validateValue[0] == "crn" { - return true, validateValue[len(validateValue)-1], nil - } else { - return false, 0, fmt.Errorf("Invalid CRN. Please pass correct CRN.") - } - } - return false, 0, nil -} diff --git a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go index d5aac2939d7..04092d0d06a 100644 --- a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go +++ b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go @@ -816,15 +816,15 @@ func parseVolAttTerraformID(s string) (string, string, error) { return segments[0], segments[1], nil } -func ValidateCRN(crn string) (bool, id, error) { +func ValidateCRN(crn string) (bool, string, error) { validInput := strings.Contains(crn, "crn:") if validInput { validateValue := strings.Split(crn, ":") if validateValue[0] == "crn" { return true, validateValue[len(validateValue)-1], nil } else { - return false, 0, fmt.Errorf("Invalid CRN. Please pass correct CRN.") + return false, "", fmt.Errorf("Invalid CRN. Please pass correct CRN.") } } - return false, 0, nil + return false, "", nil } diff --git a/ibm/service/vpc/resource_ibm_is_volume.go b/ibm/service/vpc/resource_ibm_is_volume.go index f5ae21be718..25996c40c29 100644 --- a/ibm/service/vpc/resource_ibm_is_volume.go +++ b/ibm/service/vpc/resource_ibm_is_volume.go @@ -1036,16 +1036,3 @@ func deleteAllSnapshots(sess *vpcv1.VpcV1, id string) error { } return nil } - -func ValidateCRN(crn string) (bool, id, error) { - validInput := strings.Contains(crn, "crn:") - if validInput { - validateValue := strings.Split(crn, ":") - if validateValue[0] == "crn" { - return true, validateValue[len(validateValue)-1], nil - } else { - return false, 0, fmt.Errorf("Invalid CRN. Please pass correct CRN.") - } - } - return false, 0, nil -} From cae84a8ae9d6fe5d793ab88b3960e3571c2a0cc9 Mon Sep 17 00:00:00 2001 From: Ujjwal Kumar Date: Fri, 31 May 2024 17:14:17 +0530 Subject: [PATCH 4/5] Separated crn logic from id for snapshot --- ibm/service/vpc/resource_ibm_is_instance.go | 41 ++++--- .../vpc/resource_ibm_is_instance_test.go | 96 +++++++++++++++ ...ource_ibm_is_instance_volume_attachment.go | 50 ++++---- ..._ibm_is_instance_volume_attachment_test.go | 112 ++++++++++++++++++ .../vpc/resource_ibm_is_snapshot_test.go | 2 +- ibm/service/vpc/resource_ibm_is_volume.go | 62 ++++++++-- .../vpc/resource_ibm_is_volume_test.go | 40 +++++++ website/docs/r/is_instance.html.markdown | 10 +- ...s_instance_volume_attachment.html.markdown | 7 +- website/docs/r/is_volume.html.markdown | 3 +- 10 files changed, 365 insertions(+), 58 deletions(-) diff --git a/ibm/service/vpc/resource_ibm_is_instance.go b/ibm/service/vpc/resource_ibm_is_instance.go index 233dc33e8fb..3ac9f5666e2 100644 --- a/ibm/service/vpc/resource_ibm_is_instance.go +++ b/ibm/service/vpc/resource_ibm_is_instance.go @@ -45,6 +45,7 @@ const ( isInstanceZone = "zone" isInstanceBootVolume = "boot_volume" isInstanceVolumeSnapshot = "snapshot" + isInstanceVolumeSnapshotCrn = "snapshot_crn" isInstanceSourceTemplate = "instance_template" isInstanceBandwidth = "bandwidth" isInstanceTotalVolumeBandwidth = "total_volume_bandwidth" @@ -217,8 +218,8 @@ func ResourceIBMISInstance() *schema.Resource { Type: schema.TypeString, ForceNew: true, Optional: true, - AtLeastOneOf: []string{isInstanceImage, isInstanceSourceTemplate, "boot_volume.0.snapshot", "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn", "boot_volume.0.volume_id"}, - ConflictsWith: []string{"boot_volume.0.snapshot", "boot_volume.0.volume_id"}, + AtLeastOneOf: []string{isInstanceImage, isInstanceSourceTemplate, "boot_volume.0.snapshot", "boot_volume.0.snapshot_crn", "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn", "boot_volume.0.volume_id"}, + ConflictsWith: []string{"boot_volume.0.snapshot", "boot_volume.0.snapshot_crn", "boot_volume.0.volume_id"}, Description: "Id of the instance template", }, isInstanceZone: { @@ -1090,8 +1091,8 @@ func ResourceIBMISInstance() *schema.Resource { ForceNew: true, Computed: true, Optional: true, - ConflictsWith: []string{"boot_volume.0.snapshot", "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn", "boot_volume.0.volume_id"}, - AtLeastOneOf: []string{isInstanceImage, isInstanceSourceTemplate, "boot_volume.0.snapshot", "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn", "boot_volume.0.volume_id"}, + ConflictsWith: []string{"boot_volume.0.snapshot", "boot_volume.0.snapshot_crn", "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn", "boot_volume.0.volume_id"}, + AtLeastOneOf: []string{isInstanceImage, isInstanceSourceTemplate, "boot_volume.0.snapshot", "boot_volume.0.snapshot_crn", "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn", "boot_volume.0.volume_id"}, RequiredWith: []string{isInstanceZone, isInstanceKeys, isInstanceVPC, isInstanceProfile}, Description: "image id", }, @@ -1109,8 +1110,8 @@ func ResourceIBMISInstance() *schema.Resource { ForceNew: true, Computed: true, RequiredWith: []string{isInstanceZone, isInstanceProfile, isInstanceKeys, isInstanceVPC}, - AtLeastOneOf: []string{isInstanceImage, isInstanceSourceTemplate, "boot_volume.0.volume_id", "boot_volume.0.snapshot", "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn"}, - ConflictsWith: []string{isInstanceImage, isInstanceSourceTemplate, "boot_volume.0.snapshot", "boot_volume.0.name", "boot_volume.0.encryption", "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn"}, + AtLeastOneOf: []string{isInstanceImage, isInstanceSourceTemplate, "boot_volume.0.volume_id", "boot_volume.0.snapshot", "boot_volume.0.snapshot_crn", "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn"}, + ConflictsWith: []string{isInstanceImage, isInstanceSourceTemplate, "boot_volume.0.snapshot", "boot_volume.0.snapshot_crn", "boot_volume.0.name", "boot_volume.0.encryption", "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn"}, Description: "The unique identifier for this volume", }, isInstanceVolAttVolAutoDelete: { @@ -1129,8 +1130,17 @@ func ResourceIBMISInstance() *schema.Resource { isInstanceVolumeSnapshot: { Type: schema.TypeString, RequiredWith: []string{isInstanceZone, isInstanceProfile, isInstanceKeys, isInstanceVPC}, - AtLeastOneOf: []string{isInstanceImage, isInstanceSourceTemplate, "boot_volume.0.snapshot", "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn", "boot_volume.0.volume_id"}, - ConflictsWith: []string{isInstanceImage, isInstanceSourceTemplate, "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn", "boot_volume.0.volume_id"}, + AtLeastOneOf: []string{isInstanceImage, isInstanceSourceTemplate, "boot_volume.0.snapshot", "boot_volume.0.snapshot_crn", "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn", "boot_volume.0.volume_id"}, + ConflictsWith: []string{isInstanceImage, isInstanceSourceTemplate, "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn", "boot_volume.0.volume_id", "boot_volume.0.snapshot_crn"}, + Optional: true, + ForceNew: true, + Computed: true, + }, + isInstanceVolumeSnapshotCrn: { + Type: schema.TypeString, + RequiredWith: []string{isInstanceZone, isInstanceProfile, isInstanceKeys, isInstanceVPC}, + AtLeastOneOf: []string{isInstanceImage, isInstanceSourceTemplate, "boot_volume.0.snapshot", "boot_volume.0.snapshot_crn", "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn", "boot_volume.0.volume_id"}, + ConflictsWith: []string{isInstanceImage, isInstanceSourceTemplate, "catalog_offering.0.offering_crn", "catalog_offering.0.version_crn", "boot_volume.0.volume_id", "boot_volume.0.snapshot"}, Optional: true, ForceNew: true, Computed: true, @@ -3066,17 +3076,17 @@ func instanceCreateBySnapshot(d *schema.ResourceData, meta interface{}, profile, snapshotId, ok := bootvol[isInstanceVolumeSnapshot] snapshotIdStr := snapshotId.(string) if snapshotIdStr != "" && ok { - isCRN, sourceSnapshotId, err := ValidateCRN(snapshotIdStr) - if err != nil { - return fmt.Errorf("[ERROR] Error invalid CRN: %q", err) - } - if isCRN { - snapshotIdStr = sourceSnapshotId - } volTemplate.SourceSnapshot = &vpcv1.SnapshotIdentity{ ID: &snapshotIdStr, } } + snapshotCrn, ok := bootvol[isInstanceVolumeSnapshotCrn] + snapshotCrnStr := snapshotCrn.(string) + if snapshotCrnStr != "" && ok { + volTemplate.SourceSnapshot = &vpcv1.SnapshotIdentity{ + CRN: &snapshotCrnStr, + } + } deleteboolIntf := bootvol[isInstanceVolAttVolAutoDelete] deletebool := deleteboolIntf.(bool) instanceproto.BootVolumeAttachment = &vpcv1.VolumeAttachmentPrototypeInstanceBySourceSnapshotContext{ @@ -4360,6 +4370,7 @@ func instanceGet(d *schema.ResourceData, meta interface{}, id string) error { } if vol.SourceSnapshot != nil { bootVol[isInstanceVolumeSnapshot] = vol.SourceSnapshot.ID + bootVol[isInstanceVolumeSnapshotCrn] = vol.SourceSnapshot.CRN } if vol.UserTags != nil { bootVol[isInstanceBootVolumeTags] = vol.UserTags diff --git a/ibm/service/vpc/resource_ibm_is_instance_test.go b/ibm/service/vpc/resource_ibm_is_instance_test.go index 9f3c74ac70a..7b18446db29 100644 --- a/ibm/service/vpc/resource_ibm_is_instance_test.go +++ b/ibm/service/vpc/resource_ibm_is_instance_test.go @@ -1003,6 +1003,44 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVE }, }) } +func TestAccIBMISInstanceSnapshotRestore_crn(t *testing.T) { + var instance, instanceRestore string + vpcname := fmt.Sprintf("tf-vpc-%d", acctest.RandIntRange(10, 100)) + name := fmt.Sprintf("tf-instnace-%d", acctest.RandIntRange(10, 100)) + subnetname := fmt.Sprintf("tf-subnet-%d", acctest.RandIntRange(10, 100)) + publicKey := strings.TrimSpace(` +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR +`) + sshname := fmt.Sprintf("tf-ssh-%d", acctest.RandIntRange(10, 100)) + snapshot := fmt.Sprintf("tf-snapshot-%d", acctest.RandIntRange(10, 100)) + vsiRestore := fmt.Sprintf("tf-instancerestore-%d", acctest.RandIntRange(10, 100)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMISInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMISInstanceSnapshotRestoreCrnConfig(vpcname, subnetname, sshname, publicKey, name, snapshot, vsiRestore), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISInstanceExists("ibm_is_instance.testacc_instance", instance), + testAccCheckIBMISInstanceExists("ibm_is_instance.testacc_instance_restore", instanceRestore), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "name", name), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance", "zone", acc.ISZoneName), + resource.TestCheckResourceAttr( + "ibm_is_snapshot.testacc_snapshot", "name", snapshot), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance_restore", "zone", acc.ISZoneName), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance_restore", "name", vsiRestore), + resource.TestCheckResourceAttr( + "ibm_is_instance.testacc_instance_restore", "boot_volume.0.name", "boot-restore"), + ), + }, + }, + }) +} func TestAccIBMISInstanceSnapshotRestore_forcenew(t *testing.T) { var instance, instanceRestore string @@ -1788,6 +1826,64 @@ func testAccCheckIBMISInstanceSnapshotRestoreConfig(vpcname, subnetname, sshname } `, vpcname, subnetname, acc.ISZoneName, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, acc.ISZoneName, snapshot, insRestore, acc.InstanceProfileName, acc.ISZoneName) } +func testAccCheckIBMISInstanceSnapshotRestoreCrnConfig(vpcname, subnetname, sshname, publicKey, name, snapshot, insRestore string) string { + return fmt.Sprintf(` + resource "ibm_is_vpc" "testacc_vpc" { + name = "%s" + } + + resource "ibm_is_subnet" "testacc_subnet" { + name = "%s" + vpc = ibm_is_vpc.testacc_vpc.id + zone = "%s" + total_ipv4_address_count = 16 + } + + resource "ibm_is_ssh_key" "testacc_sshkey" { + name = "%s" + public_key = "%s" + } + + resource "ibm_is_instance" "testacc_instance" { + name = "%s" + image = "%s" + profile = "%s" + primary_network_interface { + subnet = ibm_is_subnet.testacc_subnet.id + } + vpc = ibm_is_vpc.testacc_vpc.id + zone = "%s" + keys = [ibm_is_ssh_key.testacc_sshkey.id] + network_interfaces { + subnet = ibm_is_subnet.testacc_subnet.id + name = "eth1" + } + } + resource "ibm_is_snapshot" "testacc_snapshot" { + name = "%s" + source_volume = ibm_is_instance.testacc_instance.volume_attachments[0].volume_id + } + + resource "ibm_is_instance" "testacc_instance_restore" { + name = "%s" + profile = "%s" + boot_volume { + name = "boot-restore" + snapshot_crn = ibm_is_snapshot.testacc_snapshot.crn + } + primary_network_interface { + subnet = ibm_is_subnet.testacc_subnet.id + } + vpc = ibm_is_vpc.testacc_vpc.id + zone = "%s" + keys = [ibm_is_ssh_key.testacc_sshkey.id] + network_interfaces { + subnet = ibm_is_subnet.testacc_subnet.id + name = "eth1" + } + } + `, vpcname, subnetname, acc.ISZoneName, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, acc.ISZoneName, snapshot, insRestore, acc.InstanceProfileName, acc.ISZoneName) +} func testAccCheckIBMISInstanceSnapshotRestoreForceNewConfig(vpcname, subnetname, sshname, publicKey, name, name2, name3, insRestore string) string { return fmt.Sprintf(` diff --git a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go index 04092d0d06a..a5db9685d02 100644 --- a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go +++ b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment.go @@ -143,7 +143,7 @@ func ResourceIBMISInstanceVolumeAttachment() *schema.Resource { Type: schema.TypeInt, Optional: true, Computed: true, - AtLeastOneOf: []string{isInstanceVolAttVol, isInstanceVolCapacity, isInstanceVolumeSnapshot}, + AtLeastOneOf: []string{isInstanceVolAttVol, isInstanceVolCapacity, isInstanceVolumeSnapshot, isInstanceVolumeSnapshotCrn}, ConflictsWith: []string{isInstanceVolAttVol}, ValidateFunc: validate.InvokeValidator("ibm_is_instance_volume_attachment", isInstanceVolCapacity), Description: "The capacity of the volume in gigabytes. The specified minimum and maximum capacity values for creating or updating volumes may expand in the future.", @@ -160,9 +160,18 @@ func ResourceIBMISInstanceVolumeAttachment() *schema.Resource { Optional: true, Computed: true, ForceNew: true, - AtLeastOneOf: []string{isInstanceVolAttVol, isInstanceVolCapacity, isInstanceVolumeSnapshot}, - ConflictsWith: []string{isInstanceVolAttVol}, - Description: "The snapshot of the volume to be attached", + AtLeastOneOf: []string{isInstanceVolAttVol, isInstanceVolCapacity, isInstanceVolumeSnapshot, isInstanceVolumeSnapshotCrn}, + ConflictsWith: []string{isInstanceVolAttVol, isInstanceVolumeSnapshotCrn}, + Description: "The snapshot ID of the volume to be attached", + }, + isInstanceVolumeSnapshotCrn: { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + AtLeastOneOf: []string{isInstanceVolAttVol, isInstanceVolCapacity, isInstanceVolumeSnapshot, isInstanceVolumeSnapshotCrn}, + ConflictsWith: []string{isInstanceVolAttVol, isInstanceVolumeSnapshot}, + Description: "The snapshot crn of the volume to be attached", }, isInstanceVolumeAttVolumeReferenceCrn: { Type: schema.TypeString, @@ -312,19 +321,19 @@ func instanceVolAttachmentCreate(d *schema.ResourceData, meta interface{}, insta } } volSnapshotStr := "" + volSnapshotCrnStr := "" if volSnapshot, ok := d.GetOk(isInstanceVolumeSnapshot); ok { volSnapshotStr = volSnapshot.(string) - isCRN, sourceSnapshotId, err := ValidateCRN(volSnapshotStr) - if err != nil { - return fmt.Errorf("[ERROR] Error invalid CRN: %q", err) - } - if isCRN { - volSnapshotStr = sourceSnapshotId - } volProtoVol.SourceSnapshot = &vpcv1.SnapshotIdentity{ ID: &volSnapshotStr, } } + if volSnapshotCrn, ok := d.GetOk(isInstanceVolumeSnapshotCrn); ok { + volSnapshotCrnStr = volSnapshotCrn.(string) + volProtoVol.SourceSnapshot = &vpcv1.SnapshotIdentity{ + CRN: &volSnapshotCrnStr, + } + } encryptionCRNStr := "" if encryptionCRN, ok := d.GetOk(isInstanceVolEncryptionKey); ok { encryptionCRNStr = encryptionCRN.(string) @@ -333,7 +342,10 @@ func instanceVolAttachmentCreate(d *schema.ResourceData, meta interface{}, insta } } var snapCapacity int64 - if volSnapshotStr != "" { + if volSnapshotStr != "" || volSnapshotCrnStr != "" { + if volSnapshotStr == "" { + volSnapshotStr = volSnapshotCrnStr[strings.LastIndex(volSnapshotCrnStr, ":")+1:] + } snapshotGet, _, err := sess.GetSnapshot(&vpcv1.GetSnapshotOptions{ ID: &volSnapshotStr, }) @@ -482,6 +494,7 @@ func instanceVolumeAttachmentGet(d *schema.ResourceData, meta interface{}, insta } if volumeDetail.SourceSnapshot != nil { d.Set(isInstanceVolumeSnapshot, *volumeDetail.SourceSnapshot.ID) + d.Set(isInstanceVolumeSnapshotCrn, *volumeDetail.SourceSnapshot.CRN) } return nil } @@ -815,16 +828,3 @@ func parseVolAttTerraformID(s string) (string, string, error) { } return segments[0], segments[1], nil } - -func ValidateCRN(crn string) (bool, string, error) { - validInput := strings.Contains(crn, "crn:") - if validInput { - validateValue := strings.Split(crn, ":") - if validateValue[0] == "crn" { - return true, validateValue[len(validateValue)-1], nil - } else { - return false, "", fmt.Errorf("Invalid CRN. Please pass correct CRN.") - } - } - return false, "", nil -} diff --git a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment_test.go b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment_test.go index bedbd571b82..7bf702bb664 100644 --- a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment_test.go +++ b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment_test.go @@ -85,6 +85,73 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVE }, }) } +func TestAccIBMISInstanceVolumeAttachment_crn(t *testing.T) { + var instanceVolAtt string + vpcname := fmt.Sprintf("tf-vpc-%d", acctest.RandIntRange(10, 100)) + name := fmt.Sprintf("tf-instnace-%d", acctest.RandIntRange(10, 100)) + subnetname := fmt.Sprintf("tf-subnet-%d", acctest.RandIntRange(10, 100)) + publicKey := strings.TrimSpace(` +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR +`) + sshname := fmt.Sprintf("tf-ssh-%d", acctest.RandIntRange(10, 100)) + attName := fmt.Sprintf("tf-volatt-%d", acctest.RandIntRange(10, 100)) + autoDelete := true + volName := fmt.Sprintf("tf-vol-%d", acctest.RandIntRange(10, 100)) + iops1 := int64(600) + iops2 := int64(900) + + capacity1 := int64(20) + capacity2 := int64(22) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMISInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMISInstanceVolumeAttachmentCrnConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName, autoDelete, capacity1, iops1), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISInstanceVolumeAttachmentExists("ibm_is_instance_volume_attachment.testacc_att", instanceVolAtt), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "name", attName), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "delete_volume_on_instance_delete", fmt.Sprintf("%t", autoDelete)), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "capacity", fmt.Sprintf("%d", capacity1)), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "iops", "600"), + ), + }, + { + Config: testAccCheckIBMISInstanceVolumeAttachmentConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName, autoDelete, capacity1, iops2), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISInstanceVolumeAttachmentExists("ibm_is_instance_volume_attachment.testacc_att", instanceVolAtt), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "name", attName), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "delete_volume_on_instance_delete", fmt.Sprintf("%t", autoDelete)), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "capacity", fmt.Sprintf("%d", capacity1)), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "iops", "900"), + ), + }, + + { + Config: testAccCheckIBMISInstanceVolumeAttachmentConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName, autoDelete, capacity2, iops2), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISInstanceVolumeAttachmentExists("ibm_is_instance_volume_attachment.testacc_att", instanceVolAtt), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "name", attName), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "delete_volume_on_instance_delete", fmt.Sprintf("%t", autoDelete)), + resource.TestCheckResourceAttr( + "ibm_is_instance_volume_attachment.testacc_att", "capacity", fmt.Sprintf("%d", capacity2)), + ), + }, + }, + }) +} func TestAccIBMISInstanceVolumeAttachment_userTag(t *testing.T) { var instanceVolAtt string @@ -242,6 +309,51 @@ func testAccCheckIBMISInstanceVolumeAttachmentConfig(vpcname, subnetname, sshnam `, vpcname, subnetname, acc.ISZoneName, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, acc.ISZoneName, attName, capacity, iops, autoDelete, volName) } +func testAccCheckIBMISInstanceVolumeAttachmentCrnConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName string, autoDelete bool, capacity, iops int64) string { + return fmt.Sprintf(` + resource "ibm_is_vpc" "testacc_vpc" { + name = "%s" + } + resource "ibm_is_subnet" "testacc_subnet" { + name = "%s" + vpc = ibm_is_vpc.testacc_vpc.id + zone = "%s" + total_ipv4_address_count = 16 + } + resource "ibm_is_ssh_key" "testacc_sshkey" { + name = "%s" + public_key = "%s" + } + resource "ibm_is_instance" "testacc_instance" { + name = "%s" + image = "%s" + profile = "%s" + primary_network_interface { + subnet = ibm_is_subnet.testacc_subnet.id + } + vpc = ibm_is_vpc.testacc_vpc.id + zone = "%s" + keys = [ibm_is_ssh_key.testacc_sshkey.id] + network_interfaces { + subnet = ibm_is_subnet.testacc_subnet.id + name = "eth1" + } + } + resource "ibm_is_snapshot" "testacc_snapshot" { + name = "tf-test-snapshot" + source_volume = ibm_is_instance.testacc_instance.volume_attachments[0].volume_id + } + resource "ibm_is_instance_volume_attachment" "testacc_att" { + instance = ibm_is_instance.testacc_instance.id + name = "%s" + profile = "general-purpose" + snapshot_crn = ibm_is_snapshot.testacc_snapshot.crn + delete_volume_on_instance_delete = %t + volume_name = "%s" + } + `, vpcname, subnetname, acc.ISZoneName, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, acc.ISZoneName, attName, autoDelete, volName) +} + func testAccCheckIBMISInstanceVolumeAttachmentUsertagConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName, usertag string, autoDelete bool, capacity, iops int64) string { return fmt.Sprintf(` resource "ibm_is_vpc" "testacc_vpc" { diff --git a/ibm/service/vpc/resource_ibm_is_snapshot_test.go b/ibm/service/vpc/resource_ibm_is_snapshot_test.go index 550e2f902ee..f92cff6bcad 100644 --- a/ibm/service/vpc/resource_ibm_is_snapshot_test.go +++ b/ibm/service/vpc/resource_ibm_is_snapshot_test.go @@ -274,7 +274,7 @@ func testAccCheckIBMISSnapshotConfig(vpcname, subnetname, sshname, publicKey, vo resource "ibm_is_snapshot" "testacc_snapshot" { name = "%s" source_volume = ibm_is_instance.testacc_instance.volume_attachments[0].volume_id -}`, vpcname, subnetname, acc.ISZoneName, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, acc.ISZoneName, sname) + }`, vpcname, subnetname, acc.ISZoneName, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, acc.ISZoneName, sname) } func testAccCheckIBMISSnapshotEncryptedConfig(vpcname, subnetname, sshname, publicKey, volname, name, sname string) string { diff --git a/ibm/service/vpc/resource_ibm_is_volume.go b/ibm/service/vpc/resource_ibm_is_volume.go index 25996c40c29..afa5fcf8d02 100644 --- a/ibm/service/vpc/resource_ibm_is_volume.go +++ b/ibm/service/vpc/resource_ibm_is_volume.go @@ -40,6 +40,7 @@ const ( isVolumeProvisioningDone = "done" isVolumeResourceGroup = "resource_group" isVolumeSourceSnapshot = "source_snapshot" + isVolumeSourceSnapshotCrn = "source_snapshot_crn" isVolumeDeleteAllSnapshots = "delete_all_snapshots" isVolumeBandwidth = "bandwidth" isVolumeAccessTags = "access_tags" @@ -127,11 +128,21 @@ func ResourceIBMISVolume() *schema.Resource { Description: "Volume capacity value", }, isVolumeSourceSnapshot: { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Computed: true, - Description: "The unique identifier for this snapshot", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + ConflictsWith: []string{isVolumeSourceSnapshotCrn}, + ValidateFunc: validate.InvokeValidator("ibm_is_volume", isVolumeSourceSnapshot), + Description: "The unique identifier for this snapshot", + }, + isVolumeSourceSnapshotCrn: { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + ConflictsWith: []string{isVolumeSourceSnapshot}, + Description: "The crn for this snapshot", }, isVolumeResourceGroup: { Type: schema.TypeString, @@ -338,7 +349,15 @@ func ResourceIBMISVolumeValidator() *validate.ResourceValidator { Regexp: `^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$`, MinValueLength: 1, MaxValueLength: 63}) - + validateSchema = append(validateSchema, + validate.ValidateSchema{ + Identifier: isVolumeSourceSnapshot, + ValidateFunctionIdentifier: validate.ValidateRegexpLen, + Type: validate.TypeString, + Optional: true, + Regexp: `^[-0-9a-z_]+$`, + MinValueLength: 1, + MaxValueLength: 64}) validateSchema = append(validateSchema, validate.ValidateSchema{ Identifier: "tags", @@ -419,15 +438,34 @@ func volCreate(d *schema.ResourceData, meta interface{}, volName, profile, zone var volCapacity int64 if sourceSnapsht, ok := d.GetOk(isVolumeSourceSnapshot); ok { sourceSnapshot := sourceSnapsht.(string) - isCRN, sourceSnapshotId, err := ValidateCRN(sourceSnapshot) + snapshotIdentity := &vpcv1.SnapshotIdentity{ + ID: &sourceSnapshot, + } + volTemplate.SourceSnapshot = snapshotIdentity + getSnapshotOptions := &vpcv1.GetSnapshotOptions{ + ID: &sourceSnapshot, + } + snapshot, response, err := sess.GetSnapshot(getSnapshotOptions) if err != nil { - return fmt.Errorf("[ERROR] Error invalid CRN: %q", err) + return fmt.Errorf("[ERROR] Error fetching snapshot %s\n%s", err, response) } - if isCRN { - sourceSnapshot = sourceSnapshotId + if (response != nil && response.StatusCode == 404) || snapshot == nil { + return fmt.Errorf("[ERROR] No snapshot found with id %s", sourceSnapshot) } + minimumCapacity := *snapshot.MinimumCapacity + if capacity, ok := d.GetOk(isVolumeCapacity); ok { + if int64(capacity.(int)) > minimumCapacity { + volCapacity = int64(capacity.(int)) + } else { + volCapacity = minimumCapacity + } + volTemplate.Capacity = &volCapacity + } + } else if sourceSnapshtCrn, ok := d.GetOk(isVolumeSourceSnapshot); ok { + sourceSnapshot := sourceSnapshtCrn.(string) + snapshotIdentity := &vpcv1.SnapshotIdentity{ - ID: &sourceSnapshot, + CRN: &sourceSnapshot, } volTemplate.SourceSnapshot = snapshotIdentity getSnapshotOptions := &vpcv1.GetSnapshotOptions{ @@ -450,7 +488,6 @@ func volCreate(d *schema.ResourceData, meta interface{}, volName, profile, zone volTemplate.Capacity = &volCapacity } } else { - if capacity, ok := d.GetOk(isVolumeCapacity); ok { if int64(capacity.(int)) > 0 { volCapacity = int64(capacity.(int)) @@ -562,6 +599,7 @@ func volGet(d *schema.ResourceData, meta interface{}, id string) error { d.Set(isVolumeCrn, *vol.CRN) if vol.SourceSnapshot != nil { d.Set(isVolumeSourceSnapshot, *vol.SourceSnapshot.ID) + d.Set(isVolumeSourceSnapshotCrn, *vol.SourceSnapshot.CRN) } d.Set(isVolumeStatus, *vol.Status) if vol.HealthState != nil { diff --git a/ibm/service/vpc/resource_ibm_is_volume_test.go b/ibm/service/vpc/resource_ibm_is_volume_test.go index d37f3b1e225..da6f147ca1d 100644 --- a/ibm/service/vpc/resource_ibm_is_volume_test.go +++ b/ibm/service/vpc/resource_ibm_is_volume_test.go @@ -78,6 +78,35 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVE }, }) } +func TestAccIBMISVolume_snapshotcrn(t *testing.T) { + var vol string + vpcname := fmt.Sprintf("tf-vpc-%d", acctest.RandIntRange(10, 100)) + name := fmt.Sprintf("tf-instnace-%d", acctest.RandIntRange(10, 100)) + subnetname := fmt.Sprintf("tf-subnet-%d", acctest.RandIntRange(10, 100)) + publicKey := strings.TrimSpace(` +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR +`) + sshname := fmt.Sprintf("tf-ssh-%d", acctest.RandIntRange(10, 100)) + volname := fmt.Sprintf("tf-vol-%d", acctest.RandIntRange(10, 100)) + name1 := fmt.Sprintf("tfsnapshotuat-%d", acctest.RandIntRange(10, 100)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMISVolumeDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMISVolumeConfigSnapshotCrn(vpcname, subnetname, sshname, publicKey, volname, name, name1), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISVolumeExists("ibm_is_volume.storage", vol), + resource.TestCheckResourceAttrSet("ibm_is_volume.storage", "health_state"), + resource.TestCheckResourceAttrSet("ibm_is_volume.storage", "health_reasons.#"), + resource.TestCheckResourceAttr( + "ibm_is_volume.storage", "name", volname), + ), + }, + }, + }) +} func TestAccIBMISVolumeUsertag_basic(t *testing.T) { var vol string name := fmt.Sprintf("tf-vol-%d", acctest.RandIntRange(10, 100)) @@ -548,3 +577,14 @@ func testAccCheckIBMISVolumeConfigSnapshot(vpcname, subnetname, sshname, publicK } `, volname, acc.ISZoneName) } +func testAccCheckIBMISVolumeConfigSnapshotCrn(vpcname, subnetname, sshname, publicKey, volname, name, name1 string) string { + + return testAccCheckIBMISSnapshotConfig(vpcname, subnetname, sshname, publicKey, volname, name, name1) + fmt.Sprintf(` + resource "ibm_is_volume" "storage" { + name = "%s" + profile = "general-purpose" + zone = "%s" + source_snapshot_crn = ibm_is_snapshot.testacc_snapshot.crn + } + `, volname, acc.ISZoneName) +} diff --git a/website/docs/r/is_instance.html.markdown b/website/docs/r/is_instance.html.markdown index 230354dc6cf..1dad5d261bd 100644 --- a/website/docs/r/is_instance.html.markdown +++ b/website/docs/r/is_instance.html.markdown @@ -516,14 +516,18 @@ Review the argument references that you can specify for your resource. - `auto_delete_volume` - (Optional, String) If set to **true**, when deleting the instance the volume will also be deleted - `encryption` - (Optional, String) The type of encryption to use for the boot volume. - `name` - (Optional, String) The name of the boot volume. - - `size` - (Optional, Integer) The size of the boot volume.(The capacity of the volume in gigabytes. This defaults to minimum capacity of the image and maximum to `250`. + - `size` - (Optional, Integer) The size of the boot volume.(The capacity of the volume in gigabytes. This defaults to minimum capacity of the image and maximum to `250`.) ~> **NOTE:** Supports only expansion on update (must be attached to a running instance and must not be less than the current volume size) - - `snapshot` - (Optional, Forces new resource, String) The snapshot id or crn of the volume to be used for creating boot volume attachment + - `snapshot` - (Optional, Forces new resource, String) The snapshot id of the snapshot to be used for creating boot volume attachment ~> **Note:** - `snapshot` conflicts with `image` id, `instance_template` , `catalog_offering` and `boot_volume.volume_id` + `snapshot` conflicts with `image` id, `instance_template` , `catalog_offering`, `boot_volume.volume_id` and `snapshot_crn` + - `snapshot_crn` - (Optional, Forces new resource, String) The crn of the snapshot to be used for creating boot volume attachment + + ~> **Note:** + `snapshot` conflicts with `image` id, `instance_template` , `catalog_offering`, `boot_volume.volume_id` and `snapshot` - `volume_id` - (Optional, Forces new resource, String) The ID of the volume to be used for creating boot volume attachment ~> **Note:** diff --git a/website/docs/r/is_instance_volume_attachment.html.markdown b/website/docs/r/is_instance_volume_attachment.html.markdown index b2b71786b24..f040cd6f538 100644 --- a/website/docs/r/is_instance_volume_attachment.html.markdown +++ b/website/docs/r/is_instance_volume_attachment.html.markdown @@ -193,7 +193,12 @@ Review the argument references that you can specify for your resource. **•** Tiered profiles [`general-purpose`, `5iops-tier`, `10iops-tier`] can be upgraded and downgraded into each other.
**•** Can be updated only if volume is attached to an running virtual server instance.
**•** Stopped instances will be started on update of volume.
-- `snapshot` - (Optional, String) The unique identifier or CRN for this snapshot from which to clone the new volume. +- `snapshot` - (Optional, String) The unique identifier for this snapshot from which to clone the new volume. + + ~> **NOTE** + **•** one of `capacity` or `snapshot` must be present for volume creation.
+ **•** If `capacity` is not present or less than `minimum_capacity` of the snapshot, `minimum_capacity` is taken as the volume capacity.
+- `snapshot_crn` - (Optional, String) The CRN for this snapshot from which to clone the new volume. ~> **NOTE** **•** one of `capacity` or `snapshot` must be present for volume creation.
diff --git a/website/docs/r/is_volume.html.markdown b/website/docs/r/is_volume.html.markdown index ed0ddcd2adf..0dbeec9f123 100644 --- a/website/docs/r/is_volume.html.markdown +++ b/website/docs/r/is_volume.html.markdown @@ -103,7 +103,8 @@ Review the argument references that you can specify for your resource. ~> **NOTE:** tiered profiles [`general-purpose`, `5iops-tier`, `10iops-tier`] can be upgraded and downgraded into each other if volume is attached to an running virtual server instance. Stopped instances will be started on update of volume. - `resource_group` - (Optional, Forces new resource, String) The resource group ID for this volume. - `resource_controller_url` - (Optional, Forces new resource, String) The URL of the IBM Cloud dashboard that can be used to explore and view details about this instance. -- `source_snapshot` - The ID or CRN of snapshot from which to clone the volume. +- `source_snapshot` - The ID of snapshot from which to clone the volume. +- `source_snapshot_crn` - The CRN of snapshot from which to clone the volume. - `tags`- (Optional, Array of Strings) A list of user tags that you want to add to your volume. (https://cloud.ibm.com/apidocs/tagging#types-of-tags) - `zone` - (Required, Forces new resource, String) The location of the volume. From aa006d3058433cfc29cd73146e076ddafbda6afc Mon Sep 17 00:00:00 2001 From: Ujjwal Kumar Date: Mon, 3 Jun 2024 09:57:36 +0530 Subject: [PATCH 5/5] fixed review comments --- ibm/service/vpc/resource_ibm_is_instance.go | 3 +- ..._ibm_is_instance_volume_attachment_test.go | 44 +++---------------- 2 files changed, 8 insertions(+), 39 deletions(-) diff --git a/ibm/service/vpc/resource_ibm_is_instance.go b/ibm/service/vpc/resource_ibm_is_instance.go index 3ac9f5666e2..e1ef23fe527 100644 --- a/ibm/service/vpc/resource_ibm_is_instance.go +++ b/ibm/service/vpc/resource_ibm_is_instance.go @@ -3812,6 +3812,7 @@ func resourceIBMisInstanceCreate(d *schema.ResourceData, meta interface{}) error zone := d.Get(isInstanceZone).(string) image := d.Get(isInstanceImage).(string) snapshot := d.Get("boot_volume.0.snapshot").(string) + snapshotcrn := d.Get("boot_volume.0.snapshot_crn").(string) volume := d.Get("boot_volume.0.volume_id").(string) template := d.Get(isInstanceSourceTemplate).(string) if catalogOfferingOk, ok := d.GetOk(isInstanceCatalogOffering); ok { @@ -3828,7 +3829,7 @@ func resourceIBMisInstanceCreate(d *schema.ResourceData, meta interface{}) error if err != nil { return err } - } else if snapshot != "" { + } else if snapshot != "" || snapshotcrn != "" { err := instanceCreateBySnapshot(d, meta, profile, name, vpcID, zone) if err != nil { return err diff --git a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment_test.go b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment_test.go index 7bf702bb664..6e99984c0fc 100644 --- a/ibm/service/vpc/resource_ibm_is_instance_volume_attachment_test.go +++ b/ibm/service/vpc/resource_ibm_is_instance_volume_attachment_test.go @@ -97,11 +97,6 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVE attName := fmt.Sprintf("tf-volatt-%d", acctest.RandIntRange(10, 100)) autoDelete := true volName := fmt.Sprintf("tf-vol-%d", acctest.RandIntRange(10, 100)) - iops1 := int64(600) - iops2 := int64(900) - - capacity1 := int64(20) - capacity2 := int64(22) resource.Test(t, resource.TestCase{ PreCheck: func() { acc.TestAccPreCheck(t) }, @@ -109,44 +104,17 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVE CheckDestroy: testAccCheckIBMISInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccCheckIBMISInstanceVolumeAttachmentCrnConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName, autoDelete, capacity1, iops1), - Check: resource.ComposeTestCheckFunc( - testAccCheckIBMISInstanceVolumeAttachmentExists("ibm_is_instance_volume_attachment.testacc_att", instanceVolAtt), - resource.TestCheckResourceAttr( - "ibm_is_instance_volume_attachment.testacc_att", "name", attName), - resource.TestCheckResourceAttr( - "ibm_is_instance_volume_attachment.testacc_att", "delete_volume_on_instance_delete", fmt.Sprintf("%t", autoDelete)), - resource.TestCheckResourceAttr( - "ibm_is_instance_volume_attachment.testacc_att", "capacity", fmt.Sprintf("%d", capacity1)), - resource.TestCheckResourceAttr( - "ibm_is_instance_volume_attachment.testacc_att", "iops", "600"), - ), - }, - { - Config: testAccCheckIBMISInstanceVolumeAttachmentConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName, autoDelete, capacity1, iops2), - Check: resource.ComposeTestCheckFunc( - testAccCheckIBMISInstanceVolumeAttachmentExists("ibm_is_instance_volume_attachment.testacc_att", instanceVolAtt), - resource.TestCheckResourceAttr( - "ibm_is_instance_volume_attachment.testacc_att", "name", attName), - resource.TestCheckResourceAttr( - "ibm_is_instance_volume_attachment.testacc_att", "delete_volume_on_instance_delete", fmt.Sprintf("%t", autoDelete)), - resource.TestCheckResourceAttr( - "ibm_is_instance_volume_attachment.testacc_att", "capacity", fmt.Sprintf("%d", capacity1)), - resource.TestCheckResourceAttr( - "ibm_is_instance_volume_attachment.testacc_att", "iops", "900"), - ), - }, - - { - Config: testAccCheckIBMISInstanceVolumeAttachmentConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName, autoDelete, capacity2, iops2), + Config: testAccCheckIBMISInstanceVolumeAttachmentCrnConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName, autoDelete), Check: resource.ComposeTestCheckFunc( testAccCheckIBMISInstanceVolumeAttachmentExists("ibm_is_instance_volume_attachment.testacc_att", instanceVolAtt), resource.TestCheckResourceAttr( "ibm_is_instance_volume_attachment.testacc_att", "name", attName), resource.TestCheckResourceAttr( "ibm_is_instance_volume_attachment.testacc_att", "delete_volume_on_instance_delete", fmt.Sprintf("%t", autoDelete)), - resource.TestCheckResourceAttr( - "ibm_is_instance_volume_attachment.testacc_att", "capacity", fmt.Sprintf("%d", capacity2)), + resource.TestCheckResourceAttrSet( + "ibm_is_instance_volume_attachment.testacc_att", "capacity"), + resource.TestCheckResourceAttrSet( + "ibm_is_instance_volume_attachment.testacc_att", "iops"), ), }, }, @@ -309,7 +277,7 @@ func testAccCheckIBMISInstanceVolumeAttachmentConfig(vpcname, subnetname, sshnam `, vpcname, subnetname, acc.ISZoneName, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, acc.ISZoneName, attName, capacity, iops, autoDelete, volName) } -func testAccCheckIBMISInstanceVolumeAttachmentCrnConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName string, autoDelete bool, capacity, iops int64) string { +func testAccCheckIBMISInstanceVolumeAttachmentCrnConfig(vpcname, subnetname, sshname, publicKey, name, attName, volName string, autoDelete bool) string { return fmt.Sprintf(` resource "ibm_is_vpc" "testacc_vpc" { name = "%s"