Skip to content

Commit

Permalink
feature(is_instance): resize boot volume
Browse files Browse the repository at this point in the history
  • Loading branch information
ujjwal-ibm committed Mar 3, 2022
1 parent 7cfc9b6 commit 4205c6b
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 36 deletions.
104 changes: 88 additions & 16 deletions ibm/service/vpc/resource_ibm_is_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const (
isInstanceStatusFailed = "failed"

isInstanceBootAttachmentName = "name"
isInstanceBootVolumeId = "volume_id"
isInstanceBootSize = "size"
isInstanceBootIOPS = "iops"
isInstanceBootEncryption = "encryption"
Expand Down Expand Up @@ -443,11 +444,10 @@ func ResourceIBMISInstance() *schema.Resource {
},

isInstanceBootVolume: {
Type: schema.TypeList,
DiffSuppressFunc: flex.ApplyOnce,
Optional: true,
Computed: true,
MaxItems: 1,
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
isInstanceBootAttachmentName: {
Expand All @@ -457,22 +457,30 @@ func ResourceIBMISInstance() *schema.Resource {
ValidateFunc: validate.InvokeValidator("ibm_is_instance", isInstanceBootAttachmentName),
},

isInstanceBootVolumeId: {
Type: schema.TypeString,
Computed: true,
},

isInstanceVolumeSnapshot: {
Type: schema.TypeString,
RequiredWith: []string{isInstanceZone, isInstancePrimaryNetworkInterface, isInstanceProfile, isInstanceKeys, isInstanceVPC},
AtLeastOneOf: []string{isInstanceImage, isInstanceSourceTemplate, "boot_volume.0.snapshot"},
ConflictsWith: []string{isInstanceImage, isInstanceSourceTemplate},
Optional: true,
ForceNew: true,
Type: schema.TypeString,
RequiredWith: []string{isInstanceZone, isInstancePrimaryNetworkInterface, isInstanceProfile, isInstanceKeys, isInstanceVPC},
AtLeastOneOf: []string{isInstanceImage, isInstanceSourceTemplate, "boot_volume.0.snapshot"},
ConflictsWith: []string{isInstanceImage, isInstanceSourceTemplate},
Optional: true,
DiffSuppressFunc: flex.ApplyOnce,
},
isInstanceBootEncryption: {
Type: schema.TypeString,
Optional: true,
Computed: true,
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: flex.ApplyOnce,
Computed: true,
},
isInstanceBootSize: {
Type: schema.TypeInt,
Computed: true,
Type: schema.TypeInt,
Optional: true,
Computed: true,
ValidateFunc: validate.InvokeValidator("ibm_is_instance", isInstanceBootSize),
},
isInstanceBootIOPS: {
Type: schema.TypeInt,
Expand Down Expand Up @@ -752,6 +760,14 @@ func ResourceIBMISInstanceValidator() *validate.ResourceValidator {
Type: validate.TypeInt,
Optional: true,
MinValue: "500"})
validateSchema = append(validateSchema,
validate.ValidateSchema{
Identifier: isInstanceBootSize,
ValidateFunctionIdentifier: validate.IntBetween,
Type: validate.TypeInt,
Optional: true,
MinValue: "1",
MaxValue: "250"})
validateSchema = append(validateSchema,
validate.ValidateSchema{
Identifier: isInstanceAction,
Expand Down Expand Up @@ -844,6 +860,12 @@ func instanceCreateByImage(d *schema.ResourceData, meta interface{}, profile, na
if namestr != "" && ok {
volTemplate.Name = &namestr
}
sizeOk, ok := bootvol[isInstanceBootSize]
size := sizeOk.(int)
if size != 0 && ok {
sizeInt64 := int64(size)
volTemplate.Capacity = &sizeInt64
}
enc, ok := bootvol[isInstanceBootEncryption]
encstr := enc.(string)
if ok && encstr != "" {
Expand Down Expand Up @@ -1091,6 +1113,12 @@ func instanceCreateByTemplate(d *schema.ResourceData, meta interface{}, profile,
if namestr != "" && ok {
volTemplate.Name = &namestr
}
sizeOk, ok := bootvol[isInstanceBootSize]
size := sizeOk.(int)
if size != 0 && ok {
sizeInt64 := int64(size)
volTemplate.Capacity = &sizeInt64
}
enc, ok := bootvol[isInstanceBootEncryption]
encstr := enc.(string)
if ok && encstr != "" {
Expand Down Expand Up @@ -1330,6 +1358,12 @@ func instanceCreateByVolume(d *schema.ResourceData, meta interface{}, profile, n
if namestr != "" && ok {
volTemplate.Name = &namestr
}
sizeOk, ok := bootvol[isInstanceBootSize]
size := sizeOk.(int)
if size != 0 && ok {
sizeInt64 := int64(size)
volTemplate.Capacity = &sizeInt64
}
enc, ok := bootvol[isInstanceBootEncryption]
encstr := enc.(string)
if ok && encstr != "" {
Expand Down Expand Up @@ -1818,6 +1852,7 @@ func instanceGet(d *schema.ResourceData, meta interface{}, id string) error {
bootVol := map[string]interface{}{}
if instance.BootVolumeAttachment.Volume != nil {
bootVol[isInstanceBootAttachmentName] = *instance.BootVolumeAttachment.Volume.Name
bootVol[isInstanceBootVolumeId] = *instance.BootVolumeAttachment.Volume.ID
options := &vpcv1.GetVolumeOptions{
ID: instance.BootVolumeAttachment.Volume.ID,
}
Expand All @@ -1832,6 +1867,9 @@ func instanceGet(d *schema.ResourceData, meta interface{}, id string) error {
if vol.EncryptionKey != nil {
bootVol[isInstanceBootEncryption] = *vol.EncryptionKey.CRN
}
if vol.SourceSnapshot != nil {
bootVol[isInstanceVolumeSnapshot] = vol.SourceSnapshot.ID
}
}
}
bootVolList = append(bootVolList, bootVol)
Expand Down Expand Up @@ -1889,6 +1927,40 @@ func instanceUpdate(d *schema.ResourceData, meta interface{}) error {
}
id := d.Id()

bootVolSize := "boot_volume.0.size"
if d.HasChange(bootVolSize) && !d.IsNewResource() {
old, new := d.GetChange(bootVolSize)
if new.(int) < old.(int) {
return fmt.Errorf("[ERROR] Error while updating boot volume size of the instance, only expansion is possible")
}
bootVol := int64(new.(int))
volId := d.Get("boot_volume.0.volume_id").(string)
updateVolumeOptions := &vpcv1.UpdateVolumeOptions{
ID: &volId,
}
volPatchModel := &vpcv1.VolumePatch{
Capacity: &bootVol,
}
volPatchModelAsPatch, err := volPatchModel.AsPatch()

if err != nil {
return (fmt.Errorf("[ERROR] Error encountered while apply as patch for boot volume of instance %s", err))
}

updateVolumeOptions.VolumePatch = volPatchModelAsPatch

vol, res, err := instanceC.UpdateVolume(updateVolumeOptions)

if vol == nil || err != nil {
return (fmt.Errorf("[ERROR] Error encountered while expanding boot volume of instance %s/n%s", err, res))
}

_, err = isWaitForVolumeAvailable(instanceC, volId, d.Timeout(schema.TimeoutUpdate))
if err != nil {
return err
}
}

if d.HasChange(isInstanceAction) && !d.IsNewResource() {

actiontype := d.Get(isInstanceAction).(string)
Expand Down
84 changes: 84 additions & 0 deletions ibm/service/vpc/resource_ibm_is_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,55 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVE
})
}

func TestAccIBMISInstance_ResizeBoot(t *testing.T) {
var instance 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))
userData1 := "a"
resize1 := int64(220)
resize2 := int64(250)

resource.Test(t, resource.TestCase{
PreCheck: func() { acc.TestAccPreCheck(t) },
Providers: acc.TestAccProviders,
CheckDestroy: testAccCheckIBMISInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckIBMISInstanceResizeConfig(vpcname, subnetname, sshname, publicKey, name, userData1, resize1),
Check: resource.ComposeTestCheckFunc(
testAccCheckIBMISInstanceExists("ibm_is_instance.testacc_instance", instance),
resource.TestCheckResourceAttr(
"ibm_is_instance.testacc_instance", "name", name),
resource.TestCheckResourceAttr(
"ibm_is_instance.testacc_instance", "user_data", userData1),
resource.TestCheckResourceAttr(
"ibm_is_instance.testacc_instance", "boot_volume.0.size", fmt.Sprintf("%d", resize1)),
resource.TestCheckResourceAttr(
"ibm_is_instance.testacc_instance", "zone", acc.ISZoneName),
),
},
{
Config: testAccCheckIBMISInstanceResizeConfig(vpcname, subnetname, sshname, publicKey, name, userData1, resize2),
Check: resource.ComposeTestCheckFunc(
testAccCheckIBMISInstanceExists("ibm_is_instance.testacc_instance", instance),
resource.TestCheckResourceAttr(
"ibm_is_instance.testacc_instance", "name", name),
resource.TestCheckResourceAttr(
"ibm_is_instance.testacc_instance", "user_data", userData1),
resource.TestCheckResourceAttr(
"ibm_is_instance.testacc_instance", "boot_volume.0.size", fmt.Sprintf("%d", resize2)),
resource.TestCheckResourceAttr(
"ibm_is_instance.testacc_instance", "zone", acc.ISZoneName),
),
},
},
})
}
func TestAccIBMISInstanceBandwidth_basic(t *testing.T) {
var instance string
vpcname := fmt.Sprintf("tf-vpc-%d", acctest.RandIntRange(10, 100))
Expand Down Expand Up @@ -698,6 +747,41 @@ func testAccCheckIBMISInstanceConfig(vpcname, subnetname, sshname, publicKey, na
}`, vpcname, subnetname, acc.ISZoneName, acc.ISCIDR, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, userData, acc.ISZoneName)
}

func testAccCheckIBMISInstanceResizeConfig(vpcname, subnetname, sshname, publicKey, name, userData string, resize 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"
ipv4_cidr_block = "%s"
}
resource "ibm_is_ssh_key" "testacc_sshkey" {
name = "%s"
public_key = "%s"
}
resource "ibm_is_instance" "testacc_instance" {
name = "%s"
image = "%s"
profile = "%s"
boot_volume {
size = %d
}
primary_network_interface {
subnet = ibm_is_subnet.testacc_subnet.id
}
user_data = "%s"
vpc = ibm_is_vpc.testacc_vpc.id
zone = "%s"
keys = [ibm_is_ssh_key.testacc_sshkey.id]
}`, vpcname, subnetname, acc.ISZoneName, acc.ISCIDR, sshname, publicKey, name, acc.IsImage, acc.InstanceProfileName, resize, userData, acc.ISZoneName)
}

func testAccCheckIBMISInstanceBandwidthConfig(vpcname, subnetname, sshname, publicKey, name string, bandwidth int) string {
return fmt.Sprintf(`
resource "ibm_is_vpc" "testacc_vpc" {
Expand Down
46 changes: 26 additions & 20 deletions website/docs/r/is_instance.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -274,30 +274,36 @@ Review the argument references that you can specify for your resource.
Nested scheme for `boot_volume`:
- `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`.

~> **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
~> **Note:**

- `snapshot` conflicts with `image` id and `instance_template`
~> **Note:**
`snapshot` conflicts with `image` id and `instance_template`
- `dedicated_host` - (Optional, Forces new resource, String) The placement restrictions to use the virtual server instance. Unique ID of the dedicated host where the instance id placed.
- `dedicated_host_group` - (Optional, Forces new resource, String) The placement restrictions to use for the virtual server instance. Unique ID of the dedicated host group where the instance is placed.
- `default_trusted_profile_auto_link` - (Optional, Forces new resource, Boolean) If set to `true`, the system will create a link to the specified `target` trusted profile during instance creation. Regardless of whether a link is created by the system or manually using the IAM Identity service, it will be automatically deleted when the instance is deleted. Default value : **true**
- `default_trusted_profile_target` - (Optional, Forces new resource, String) The unique identifier or CRN of the default IAM trusted profile to use for this virtual server instance.
- `force_action` - (Optional, Boolean) Required with `action`. If set to `true`, the action will be forced immediately, and all queued actions deleted. Ignored for the start action.
- `force_recovery_time` - (Optional, Integer) Define timeout (in minutes), to force the `is_instance` to recover from a perpetual "starting" state, during provisioning. And to force the is_instance to recover from a perpetual "stopping" state, during removal of user access. ~>**Note:** The force_recovery_time is used to retry multiple times until timeout.
- `image` - (Optional, String) The ID of the virtual server image that you want to use. To list supported images, run `ibmcloud is images`.
~> **Note:**

- `image` conflicts with `boot_volume.0.snapshot`
- `force_recovery_time` - (Optional, Integer) Define timeout (in minutes), to force the `is_instance` to recover from a perpetual "starting" state, during provisioning. And to force the is_instance to recover from a perpetual "stopping" state, during removal of user access.

~>**Note:** The force_recovery_time is used to retry multiple times until timeout.
- `image` - (Optional, String) The ID of the virtual server image that you want to use. To list supported images, run `ibmcloud is images` or use `ibm_is_images` datasource.

~> **Note:**
`image` conflicts with `boot_volume.0.snapshot`
- `keys` - (Optional, List) A comma-separated list of SSH keys that you want to add to your instance.
- `metadata_service_enabled` - (Optional, Boolean) Indicates whether the metadata service endpoint is available to the virtual server instance. Default value : **false**
- `name` - (Optional, String) The instance name.
- `network_interfaces` (Optional, Forces new resource, List) A list of more network interfaces that are set up for the instance.

Nested scheme for `network_interfaces`:
- `allow_ip_spoofing`- (Optional, Bool) Indicates whether IP spoofing is allowed on the interface. If **false**, IP spoofing is prevented on the interface. If **true**, IP spoofing is allowed on the interface.
~> **NOTE:**:
- `allow_ip_spoofing` requires **IP spoofing operator** access under VPC infrastructure Services. As the **IP spoofing operator**, you can enable or disable the IP spoofing check on virtual server instances.
- Use this only if you have **IP spoofing operator** access.
~> **NOTE:**
`allow_ip_spoofing` requires **IP spoofing operator** access under VPC infrastructure Services. As the **IP spoofing operator**, you can enable or disable the IP spoofing check on virtual server instances. Use this only if you have **IP spoofing operator** access.

- `name` - (Optional, String) The name of the network interface.
- `primary_ipv4_address` - (Optional, Forces new resource, String) The IPV4 address of the interface.
Expand All @@ -308,9 +314,9 @@ Review the argument references that you can specify for your resource.

Nested scheme for `primary_network_interface`:
- `allow_ip_spoofing`- (Optional, Bool) Indicates whether IP spoofing is allowed on the interface. If **false**, IP spoofing is prevented on the interface. If **true**, IP spoofing is allowed on the interface.
~> **NOTE:**:
- `allow_ip_spoofing` requires **IP spoofing operator** access under VPC infrastructure Services. As the **IP spoofing operator**, you can enable or disable the IP spoofing check on virtual server instances.
- Use this only if you have **IP spoofing operator** access.

~> **NOTE:**
`allow_ip_spoofing` requires **IP spoofing operator** access under VPC infrastructure Services. As the **IP spoofing operator**, you can enable or disable the IP spoofing check on virtual server instances. Use this only if you have **IP spoofing operator** access.

- `name` - (Optional, String) The name of the network interface.
- `port_speed` - (Deprecated, Integer) Speed of the network interface.
Expand All @@ -319,15 +325,15 @@ Review the argument references that you can specify for your resource.
- `security_groups`-List of strings-Optional-A comma separated list of security groups to add to the primary network interface.
- `profile` - (Optional, String) The name of the profile that you want to use for your instance. To list supported profiles, run `ibmcloud is instance-profiles`.

~>**NOTE:**
When the `profile` is changed, the VSI is restarted. The new profile must:
1. Have matching instance disk support. Any disks associated with the current profile will be deleted, and any disks associated with the requested profile will be created.
2. Be compatible with any placement_target(`dedicated_host`, `dedicated_host_group`, `placement_group`) constraints. For example, if the instance is placed on a dedicated host, the requested profile family must be the same as the dedicated host family.
**NOTE:**
When the `profile` is changed, the VSI is restarted. The new profile must:
1. Have matching instance disk support. Any disks associated with the current profile will be deleted, and any disks associated with the requested profile will be created.
2. Be compatible with any placement_target(`dedicated_host`, `dedicated_host_group`, `placement_group`) constraints. For example, if the instance is placed on a dedicated host, the requested profile family must be the same as the dedicated host family.
- `resource_group` - (Optional, Forces new resource, String) The ID of the resource group where you want to create the instance.
- `instance_template` - (Optional, String) ID of the source template.
~> **Note:**
- `instance_template` conflicts with `boot_volume.0.snapshot`

~> **Note:**
`instance_template` conflicts with `boot_volume.0.snapshot`
- `tags` (Optional, Array of Strings) A list of tags that you want to add to your instance. Tags can help you find your instance more easily later.
- `total_volume_bandwidth` - (Optional, Integer) The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes
- `user_data` - (Optional, String) User data to transfer to the instance.
Expand Down

0 comments on commit 4205c6b

Please sign in to comment.