Skip to content

Commit d681c25

Browse files
committed
provider/openstack: multi ephemeral support
1 parent d2a188c commit d681c25

File tree

2 files changed

+105
-34
lines changed

2 files changed

+105
-34
lines changed

builtin/providers/openstack/resource_openstack_compute_instance_v2.go

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -181,13 +181,13 @@ func resourceComputeInstanceV2() *schema.Resource {
181181
ForceNew: true,
182182
Elem: &schema.Resource{
183183
Schema: map[string]*schema.Schema{
184-
"uuid": &schema.Schema{
184+
"source_type": &schema.Schema{
185185
Type: schema.TypeString,
186186
Required: true,
187187
},
188-
"source_type": &schema.Schema{
188+
"uuid": &schema.Schema{
189189
Type: schema.TypeString,
190-
Required: true,
190+
Optional: true,
191191
},
192192
"volume_size": &schema.Schema{
193193
Type: schema.TypeInt,
@@ -206,6 +206,10 @@ func resourceComputeInstanceV2() *schema.Resource {
206206
Optional: true,
207207
Default: false,
208208
},
209+
"guest_format": &schema.Schema{
210+
Type: schema.TypeString,
211+
Optional: true,
212+
},
209213
},
210214
},
211215
},
@@ -344,14 +348,10 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
344348
}
345349

346350
if vL, ok := d.GetOk("block_device"); ok {
347-
for _, v := range vL.([]interface{}) {
348-
blockDeviceRaw := v.(map[string]interface{})
349-
blockDevice := resourceInstanceBlockDeviceV2(d, blockDeviceRaw)
350-
createOpts = &bootfromvolume.CreateOptsExt{
351-
createOpts,
352-
blockDevice,
353-
}
354-
log.Printf("[DEBUG] Create BFV Options: %+v", createOpts)
351+
blockDevices := resourceInstanceBlockDevicesV2(d, vL.([]interface{}))
352+
createOpts = &bootfromvolume.CreateOptsExt{
353+
createOpts,
354+
blockDevices,
355355
}
356356
}
357357

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

929-
func resourceInstanceBlockDeviceV2(d *schema.ResourceData, bd map[string]interface{}) []bootfromvolume.BlockDevice {
930-
sourceType := bootfromvolume.SourceType(bd["source_type"].(string))
931-
bfvOpts := []bootfromvolume.BlockDevice{
932-
bootfromvolume.BlockDevice{
933-
UUID: bd["uuid"].(string),
929+
func resourceInstanceBlockDevicesV2(d *schema.ResourceData, bds []interface{}) []bootfromvolume.BlockDevice {
930+
blockDeviceOpts := make([]bootfromvolume.BlockDevice, len(bds))
931+
for i, bd := range bds {
932+
bdM := bd.(map[string]interface{})
933+
sourceType := bootfromvolume.SourceType(bdM["source_type"].(string))
934+
blockDeviceOpts[i] = bootfromvolume.BlockDevice{
935+
UUID: bdM["uuid"].(string),
934936
SourceType: sourceType,
935-
VolumeSize: bd["volume_size"].(int),
936-
DestinationType: bd["destination_type"].(string),
937-
BootIndex: bd["boot_index"].(int),
938-
DeleteOnTermination: bd["delete_on_termination"].(bool),
939-
},
937+
VolumeSize: bdM["volume_size"].(int),
938+
DestinationType: bdM["destination_type"].(string),
939+
BootIndex: bdM["boot_index"].(int),
940+
DeleteOnTermination: bdM["delete_on_termination"].(bool),
941+
GuestFormat: bdM["guest_format"].(string),
942+
}
940943
}
941944

942-
return bfvOpts
945+
log.Printf("[DEBUG] Block Device Options: %+v", blockDeviceOpts)
946+
return blockDeviceOpts
943947
}
944948

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

979983
func getImageIDFromConfig(computeClient *gophercloud.ServiceClient, d *schema.ResourceData) (string, error) {
980-
// If block_device was used, an Image does not need to be specified.
981-
// If an Image was specified, ignore it
982-
if _, ok := d.GetOk("block_device"); ok {
983-
return "", nil
984+
// If block_device was used, an Image does not need to be specified, unless an image/local
985+
// combination was used. This emulates normal boot behavior. Otherwise, ignore the image altogether.
986+
if vL, ok := d.GetOk("block_device"); ok {
987+
needImage := false
988+
for _, v := range vL.([]interface{}) {
989+
vM := v.(map[string]interface{})
990+
if vM["source_type"] == "image" && vM["destination_type"] == "local" {
991+
needImage = true
992+
}
993+
}
994+
if !needImage {
995+
return "", nil
996+
}
984997
}
985998

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

10141027
func setImageInformation(computeClient *gophercloud.ServiceClient, server *servers.Server, d *schema.ResourceData) error {
1015-
// If block_device was used, an Image does not need to be specified.
1016-
// If an Image was specified, ignore it
1017-
if _, ok := d.GetOk("block_device"); ok {
1018-
d.Set("image_id", "Attempt to boot from volume - no image supplied")
1019-
return nil
1028+
// If block_device was used, an Image does not need to be specified, unless an image/local
1029+
// combination was used. This emulates normal boot behavior. Otherwise, ignore the image altogether.
1030+
if vL, ok := d.GetOk("block_device"); ok {
1031+
needImage := false
1032+
for _, v := range vL.([]interface{}) {
1033+
vM := v.(map[string]interface{})
1034+
if vM["source_type"] == "image" && vM["destination_type"] == "local" {
1035+
needImage = true
1036+
}
1037+
}
1038+
if !needImage {
1039+
d.Set("image_id", "Attempt to boot from volume - no image supplied")
1040+
return nil
1041+
}
10201042
}
10211043

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

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

builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,6 @@ func TestAccComputeV2Instance_bootFromVolumeImage(t *testing.T) {
263263
uuid = "%s"
264264
source_type = "image"
265265
volume_size = 5
266-
boot_index = 0
267266
destination_type = "volume"
268267
delete_on_termination = true
269268
}
@@ -325,6 +324,53 @@ func TestAccComputeV2Instance_bootFromVolumeVolume(t *testing.T) {
325324
})
326325
}
327326

327+
func TestAccComputeV2Instance_multiEphemeral(t *testing.T) {
328+
var instance servers.Server
329+
var testAccComputeV2Instance_multiEphemeral = fmt.Sprintf(`
330+
resource "openstack_compute_instance_v2" "foo" {
331+
name = "terraform-test"
332+
security_groups = ["default"]
333+
block_device {
334+
boot_index = 0
335+
delete_on_termination = true
336+
destination_type = "local"
337+
source_type = "image"
338+
uuid = "%s"
339+
}
340+
block_device {
341+
boot_index = -1
342+
delete_on_termination = true
343+
destination_type = "local"
344+
guest_format = "ext4"
345+
source_type = "blank"
346+
volume_size = 1
347+
}
348+
block_device {
349+
boot_index = -1
350+
delete_on_termination = true
351+
destination_type = "local"
352+
guest_format = "ext4"
353+
source_type = "blank"
354+
volume_size = 1
355+
}
356+
}`,
357+
os.Getenv("OS_IMAGE_ID"))
358+
359+
resource.Test(t, resource.TestCase{
360+
PreCheck: func() { testAccPreCheck(t) },
361+
Providers: testAccProviders,
362+
CheckDestroy: testAccCheckComputeV2InstanceDestroy,
363+
Steps: []resource.TestStep{
364+
resource.TestStep{
365+
Config: testAccComputeV2Instance_multiEphemeral,
366+
Check: resource.ComposeTestCheckFunc(
367+
testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.foo", &instance),
368+
),
369+
},
370+
},
371+
})
372+
}
373+
328374
func testAccCheckComputeV2InstanceDestroy(s *terraform.State) error {
329375
config := testAccProvider.Meta().(*Config)
330376
computeClient, err := config.computeV2Client(OS_REGION_NAME)

0 commit comments

Comments
 (0)