Skip to content

Commit

Permalink
provider/openstack: multi ephemeral support
Browse files Browse the repository at this point in the history
  • Loading branch information
jtopjian committed Dec 12, 2015
1 parent d2a188c commit d681c25
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,13 @@ func resourceComputeInstanceV2() *schema.Resource {
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"uuid": &schema.Schema{
"source_type": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"source_type": &schema.Schema{
"uuid": &schema.Schema{
Type: schema.TypeString,
Required: true,
Optional: true,
},
"volume_size": &schema.Schema{
Type: schema.TypeInt,
Expand All @@ -206,6 +206,10 @@ func resourceComputeInstanceV2() *schema.Resource {
Optional: true,
Default: false,
},
"guest_format": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
},
},
},
Expand Down Expand Up @@ -344,14 +348,10 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
}

if vL, ok := d.GetOk("block_device"); ok {
for _, v := range vL.([]interface{}) {
blockDeviceRaw := v.(map[string]interface{})
blockDevice := resourceInstanceBlockDeviceV2(d, blockDeviceRaw)
createOpts = &bootfromvolume.CreateOptsExt{
createOpts,
blockDevice,
}
log.Printf("[DEBUG] Create BFV Options: %+v", createOpts)
blockDevices := resourceInstanceBlockDevicesV2(d, vL.([]interface{}))
createOpts = &bootfromvolume.CreateOptsExt{
createOpts,
blockDevices,
}
}

Expand Down Expand Up @@ -926,20 +926,24 @@ func resourceInstanceMetadataV2(d *schema.ResourceData) map[string]string {
return m
}

func resourceInstanceBlockDeviceV2(d *schema.ResourceData, bd map[string]interface{}) []bootfromvolume.BlockDevice {
sourceType := bootfromvolume.SourceType(bd["source_type"].(string))
bfvOpts := []bootfromvolume.BlockDevice{
bootfromvolume.BlockDevice{
UUID: bd["uuid"].(string),
func resourceInstanceBlockDevicesV2(d *schema.ResourceData, bds []interface{}) []bootfromvolume.BlockDevice {
blockDeviceOpts := make([]bootfromvolume.BlockDevice, len(bds))
for i, bd := range bds {
bdM := bd.(map[string]interface{})
sourceType := bootfromvolume.SourceType(bdM["source_type"].(string))
blockDeviceOpts[i] = bootfromvolume.BlockDevice{
UUID: bdM["uuid"].(string),
SourceType: sourceType,
VolumeSize: bd["volume_size"].(int),
DestinationType: bd["destination_type"].(string),
BootIndex: bd["boot_index"].(int),
DeleteOnTermination: bd["delete_on_termination"].(bool),
},
VolumeSize: bdM["volume_size"].(int),
DestinationType: bdM["destination_type"].(string),
BootIndex: bdM["boot_index"].(int),
DeleteOnTermination: bdM["delete_on_termination"].(bool),
GuestFormat: bdM["guest_format"].(string),
}
}

return bfvOpts
log.Printf("[DEBUG] Block Device Options: %+v", blockDeviceOpts)
return blockDeviceOpts
}

func resourceInstanceSchedulerHintsV2(d *schema.ResourceData, schedulerHintsRaw map[string]interface{}) schedulerhints.SchedulerHints {
Expand Down Expand Up @@ -977,10 +981,19 @@ func resourceInstanceSchedulerHintsV2(d *schema.ResourceData, schedulerHintsRaw
}

func getImageIDFromConfig(computeClient *gophercloud.ServiceClient, d *schema.ResourceData) (string, error) {
// If block_device was used, an Image does not need to be specified.
// If an Image was specified, ignore it
if _, ok := d.GetOk("block_device"); ok {
return "", nil
// If block_device was used, an Image does not need to be specified, unless an image/local
// combination was used. This emulates normal boot behavior. Otherwise, ignore the image altogether.
if vL, ok := d.GetOk("block_device"); ok {
needImage := false
for _, v := range vL.([]interface{}) {
vM := v.(map[string]interface{})
if vM["source_type"] == "image" && vM["destination_type"] == "local" {
needImage = true
}
}
if !needImage {
return "", nil
}
}

if imageId := d.Get("image_id").(string); imageId != "" {
Expand Down Expand Up @@ -1012,11 +1025,20 @@ func getImageIDFromConfig(computeClient *gophercloud.ServiceClient, d *schema.Re
}

func setImageInformation(computeClient *gophercloud.ServiceClient, server *servers.Server, d *schema.ResourceData) error {
// If block_device was used, an Image does not need to be specified.
// If an Image was specified, ignore it
if _, ok := d.GetOk("block_device"); ok {
d.Set("image_id", "Attempt to boot from volume - no image supplied")
return nil
// If block_device was used, an Image does not need to be specified, unless an image/local
// combination was used. This emulates normal boot behavior. Otherwise, ignore the image altogether.
if vL, ok := d.GetOk("block_device"); ok {
needImage := false
for _, v := range vL.([]interface{}) {
vM := v.(map[string]interface{})
if vM["source_type"] == "image" && vM["destination_type"] == "local" {
needImage = true
}
}
if !needImage {
d.Set("image_id", "Attempt to boot from volume - no image supplied")
return nil
}
}

imageId := server.Image["id"].(string)
Expand Down Expand Up @@ -1230,8 +1252,11 @@ func checkVolumeConfig(d *schema.ResourceData) error {
}

if vL, ok := d.GetOk("block_device"); ok {
if len(vL.([]interface{})) > 1 {
return fmt.Errorf("Can only specify one block device to boot from.")
for _, v := range vL.([]interface{}) {
vM := v.(map[string]interface{})
if vM["source_type"] != "blank" && vM["uuid"] == "" {
return fmt.Errorf("You must specify a uuid for %s block device types", vM["source_type"])
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,6 @@ func TestAccComputeV2Instance_bootFromVolumeImage(t *testing.T) {
uuid = "%s"
source_type = "image"
volume_size = 5
boot_index = 0
destination_type = "volume"
delete_on_termination = true
}
Expand Down Expand Up @@ -325,6 +324,53 @@ func TestAccComputeV2Instance_bootFromVolumeVolume(t *testing.T) {
})
}

func TestAccComputeV2Instance_multiEphemeral(t *testing.T) {
var instance servers.Server
var testAccComputeV2Instance_multiEphemeral = fmt.Sprintf(`
resource "openstack_compute_instance_v2" "foo" {
name = "terraform-test"
security_groups = ["default"]
block_device {
boot_index = 0
delete_on_termination = true
destination_type = "local"
source_type = "image"
uuid = "%s"
}
block_device {
boot_index = -1
delete_on_termination = true
destination_type = "local"
guest_format = "ext4"
source_type = "blank"
volume_size = 1
}
block_device {
boot_index = -1
delete_on_termination = true
destination_type = "local"
guest_format = "ext4"
source_type = "blank"
volume_size = 1
}
}`,
os.Getenv("OS_IMAGE_ID"))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeV2InstanceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeV2Instance_multiEphemeral,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.foo", &instance),
),
},
},
})
}

func testAccCheckComputeV2InstanceDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
computeClient, err := config.computeV2Client(OS_REGION_NAME)
Expand Down

0 comments on commit d681c25

Please sign in to comment.