github.com/bpineau/terraform@v0.8.0-rc1.0.20161126184705-a8886012d185/builtin/providers/openstack/resource_openstack_compute_volume_attach_v2.go (about) 1 package openstack 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 "time" 8 9 "github.com/gophercloud/gophercloud" 10 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" 11 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 ) 15 16 func resourceComputeVolumeAttachV2() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceComputeVolumeAttachV2Create, 19 Read: resourceComputeVolumeAttachV2Read, 20 Delete: resourceComputeVolumeAttachV2Delete, 21 Importer: &schema.ResourceImporter{ 22 State: schema.ImportStatePassthrough, 23 }, 24 25 Schema: map[string]*schema.Schema{ 26 "region": &schema.Schema{ 27 Type: schema.TypeString, 28 Required: true, 29 ForceNew: true, 30 DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), 31 }, 32 33 "instance_id": &schema.Schema{ 34 Type: schema.TypeString, 35 Required: true, 36 ForceNew: true, 37 }, 38 39 "volume_id": &schema.Schema{ 40 Type: schema.TypeString, 41 Required: true, 42 ForceNew: true, 43 }, 44 45 "device": &schema.Schema{ 46 Type: schema.TypeString, 47 Computed: true, 48 Optional: true, 49 }, 50 }, 51 } 52 } 53 54 func resourceComputeVolumeAttachV2Create(d *schema.ResourceData, meta interface{}) error { 55 config := meta.(*Config) 56 computeClient, err := config.computeV2Client(d.Get("region").(string)) 57 if err != nil { 58 return fmt.Errorf("Error creating OpenStack compute client: %s", err) 59 } 60 61 instanceId := d.Get("instance_id").(string) 62 volumeId := d.Get("volume_id").(string) 63 64 var device string 65 if v, ok := d.GetOk("device"); ok { 66 device = v.(string) 67 } 68 69 attachOpts := volumeattach.CreateOpts{ 70 Device: device, 71 VolumeID: volumeId, 72 } 73 74 log.Printf("[DEBUG] Creating volume attachment: %#v", attachOpts) 75 76 attachment, err := volumeattach.Create(computeClient, instanceId, attachOpts).Extract() 77 if err != nil { 78 return err 79 } 80 81 log.Printf("[DEBUG] Created volume attachment: %#v", attachment) 82 83 // Use the instance ID and attachment ID as the resource ID. 84 // This is because an attachment cannot be retrieved just by its ID alone. 85 id := fmt.Sprintf("%s/%s", instanceId, attachment.ID) 86 87 d.SetId(id) 88 89 return resourceComputeVolumeAttachV2Read(d, meta) 90 } 91 92 func resourceComputeVolumeAttachV2Read(d *schema.ResourceData, meta interface{}) error { 93 config := meta.(*Config) 94 computeClient, err := config.computeV2Client(d.Get("region").(string)) 95 if err != nil { 96 return fmt.Errorf("Error creating OpenStack compute client: %s", err) 97 } 98 99 instanceId, attachmentId, err := parseComputeVolumeAttachmentId(d.Id()) 100 if err != nil { 101 return err 102 } 103 104 attachment, err := volumeattach.Get(computeClient, instanceId, attachmentId).Extract() 105 if err != nil { 106 return err 107 } 108 109 log.Printf("[DEBUG] Retrieved volume attachment: %#v", attachment) 110 111 d.Set("instance_id", attachment.ServerID) 112 d.Set("volume_id", attachment.VolumeID) 113 d.Set("device", attachment.Device) 114 115 return nil 116 } 117 118 func resourceComputeVolumeAttachV2Delete(d *schema.ResourceData, meta interface{}) error { 119 config := meta.(*Config) 120 computeClient, err := config.computeV2Client(d.Get("region").(string)) 121 if err != nil { 122 return fmt.Errorf("Error creating OpenStack compute client: %s", err) 123 } 124 125 instanceId, attachmentId, err := parseComputeVolumeAttachmentId(d.Id()) 126 if err != nil { 127 return err 128 } 129 130 stateConf := &resource.StateChangeConf{ 131 Pending: []string{""}, 132 Target: []string{"DETACHED"}, 133 Refresh: volumeDetachRefreshFunc(computeClient, instanceId, attachmentId), 134 Timeout: 10 * time.Minute, 135 Delay: 15 * time.Second, 136 MinTimeout: 15 * time.Second, 137 } 138 139 if _, err = stateConf.WaitForState(); err != nil { 140 return fmt.Errorf("Error detaching OpenStack volume: %s", err) 141 } 142 143 return nil 144 } 145 146 func volumeDetachRefreshFunc(computeClient *gophercloud.ServiceClient, instanceId, attachmentId string) resource.StateRefreshFunc { 147 return func() (interface{}, string, error) { 148 log.Printf("[DEBUG] Attempting to detach OpenStack volume %s from instance %s", attachmentId, instanceId) 149 150 va, err := volumeattach.Get(computeClient, instanceId, attachmentId).Extract() 151 if err != nil { 152 if _, ok := err.(gophercloud.ErrDefault404); ok { 153 return va, "DETACHED", nil 154 } 155 return va, "", err 156 } 157 158 err = volumeattach.Delete(computeClient, instanceId, attachmentId).ExtractErr() 159 if err != nil { 160 if _, ok := err.(gophercloud.ErrDefault404); ok { 161 return va, "DETACHED", nil 162 } 163 164 if _, ok := err.(gophercloud.ErrDefault400); ok { 165 return nil, "", nil 166 } 167 168 return nil, "", err 169 } 170 171 log.Printf("[DEBUG] OpenStack Volume Attachment (%s) is still active.", attachmentId) 172 return nil, "", nil 173 } 174 } 175 176 func parseComputeVolumeAttachmentId(id string) (string, string, error) { 177 idParts := strings.Split(id, "/") 178 if len(idParts) < 2 { 179 return "", "", fmt.Errorf("Unable to determine volume attachment ID") 180 } 181 182 instanceId := idParts[0] 183 attachmentId := idParts[1] 184 185 return instanceId, attachmentId, nil 186 }