mirror of
https://github.com/opentffoundation/opentf.git
synced 2026-03-28 23:01:14 -04:00
* provider/openstack: Detect Region for Importing Resources This commit changes the way the OpenStack region is detected and set. Any time a region is required, the region attribute will first be checked. Next, the OS_REGION_NAME environment variable will be checked. While schema.EnvDefaultFunc handles this same situation, it is not applicable when importing resources. * provider/openstack: No longer ignore region in importing tests * provider/openstack: Network and Subnet Import Fixes This commit fixes the OpenStack Network and Subnet resources so that importing of those resources is successful.
188 lines
4.8 KiB
Go
188 lines
4.8 KiB
Go
package openstack
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gophercloud/gophercloud"
|
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
|
|
|
|
"github.com/hashicorp/terraform/helper/resource"
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
)
|
|
|
|
func resourceComputeVolumeAttachV2() *schema.Resource {
|
|
return &schema.Resource{
|
|
Create: resourceComputeVolumeAttachV2Create,
|
|
Read: resourceComputeVolumeAttachV2Read,
|
|
Delete: resourceComputeVolumeAttachV2Delete,
|
|
Importer: &schema.ResourceImporter{
|
|
State: schema.ImportStatePassthrough,
|
|
},
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
"region": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
|
|
},
|
|
|
|
"instance_id": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
},
|
|
|
|
"volume_id": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ForceNew: true,
|
|
},
|
|
|
|
"device": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Computed: true,
|
|
Optional: true,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func resourceComputeVolumeAttachV2Create(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
computeClient, err := config.computeV2Client(GetRegion(d))
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
|
|
}
|
|
|
|
instanceId := d.Get("instance_id").(string)
|
|
volumeId := d.Get("volume_id").(string)
|
|
|
|
var device string
|
|
if v, ok := d.GetOk("device"); ok {
|
|
device = v.(string)
|
|
}
|
|
|
|
attachOpts := volumeattach.CreateOpts{
|
|
Device: device,
|
|
VolumeID: volumeId,
|
|
}
|
|
|
|
log.Printf("[DEBUG] Creating volume attachment: %#v", attachOpts)
|
|
|
|
attachment, err := volumeattach.Create(computeClient, instanceId, attachOpts).Extract()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Printf("[DEBUG] Created volume attachment: %#v", attachment)
|
|
|
|
// Use the instance ID and attachment ID as the resource ID.
|
|
// This is because an attachment cannot be retrieved just by its ID alone.
|
|
id := fmt.Sprintf("%s/%s", instanceId, attachment.ID)
|
|
|
|
d.SetId(id)
|
|
|
|
return resourceComputeVolumeAttachV2Read(d, meta)
|
|
}
|
|
|
|
func resourceComputeVolumeAttachV2Read(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
computeClient, err := config.computeV2Client(GetRegion(d))
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
|
|
}
|
|
|
|
instanceId, attachmentId, err := parseComputeVolumeAttachmentId(d.Id())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
attachment, err := volumeattach.Get(computeClient, instanceId, attachmentId).Extract()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Printf("[DEBUG] Retrieved volume attachment: %#v", attachment)
|
|
|
|
d.Set("instance_id", attachment.ServerID)
|
|
d.Set("volume_id", attachment.VolumeID)
|
|
d.Set("device", attachment.Device)
|
|
d.Set("region", GetRegion(d))
|
|
|
|
return nil
|
|
}
|
|
|
|
func resourceComputeVolumeAttachV2Delete(d *schema.ResourceData, meta interface{}) error {
|
|
config := meta.(*Config)
|
|
computeClient, err := config.computeV2Client(GetRegion(d))
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
|
|
}
|
|
|
|
instanceId, attachmentId, err := parseComputeVolumeAttachmentId(d.Id())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
stateConf := &resource.StateChangeConf{
|
|
Pending: []string{""},
|
|
Target: []string{"DETACHED"},
|
|
Refresh: volumeDetachRefreshFunc(computeClient, instanceId, attachmentId),
|
|
Timeout: 10 * time.Minute,
|
|
Delay: 15 * time.Second,
|
|
MinTimeout: 15 * time.Second,
|
|
}
|
|
|
|
if _, err = stateConf.WaitForState(); err != nil {
|
|
return fmt.Errorf("Error detaching OpenStack volume: %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func volumeDetachRefreshFunc(computeClient *gophercloud.ServiceClient, instanceId, attachmentId string) resource.StateRefreshFunc {
|
|
return func() (interface{}, string, error) {
|
|
log.Printf("[DEBUG] Attempting to detach OpenStack volume %s from instance %s", attachmentId, instanceId)
|
|
|
|
va, err := volumeattach.Get(computeClient, instanceId, attachmentId).Extract()
|
|
if err != nil {
|
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
|
return va, "DETACHED", nil
|
|
}
|
|
return va, "", err
|
|
}
|
|
|
|
err = volumeattach.Delete(computeClient, instanceId, attachmentId).ExtractErr()
|
|
if err != nil {
|
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
|
return va, "DETACHED", nil
|
|
}
|
|
|
|
if _, ok := err.(gophercloud.ErrDefault400); ok {
|
|
return nil, "", nil
|
|
}
|
|
|
|
return nil, "", err
|
|
}
|
|
|
|
log.Printf("[DEBUG] OpenStack Volume Attachment (%s) is still active.", attachmentId)
|
|
return nil, "", nil
|
|
}
|
|
}
|
|
|
|
func parseComputeVolumeAttachmentId(id string) (string, string, error) {
|
|
idParts := strings.Split(id, "/")
|
|
if len(idParts) < 2 {
|
|
return "", "", fmt.Errorf("Unable to determine volume attachment ID")
|
|
}
|
|
|
|
instanceId := idParts[0]
|
|
attachmentId := idParts[1]
|
|
|
|
return instanceId, attachmentId, nil
|
|
}
|