github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/builtin/providers/scaleway/resource_scaleway_volume_attachment.go (about) 1 package scaleway 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/hashicorp/terraform/helper/resource" 9 "github.com/hashicorp/terraform/helper/schema" 10 "github.com/scaleway/scaleway-cli/pkg/api" 11 ) 12 13 func resourceScalewayVolumeAttachment() *schema.Resource { 14 return &schema.Resource{ 15 Create: resourceScalewayVolumeAttachmentCreate, 16 Read: resourceScalewayVolumeAttachmentRead, 17 Delete: resourceScalewayVolumeAttachmentDelete, 18 Schema: map[string]*schema.Schema{ 19 "server": { 20 Type: schema.TypeString, 21 Required: true, 22 ForceNew: true, 23 }, 24 "volume": { 25 Type: schema.TypeString, 26 Required: true, 27 ForceNew: true, 28 }, 29 }, 30 } 31 } 32 33 var errVolumeAlreadyAttached = fmt.Errorf("Scaleway volume already attached") 34 35 func resourceScalewayVolumeAttachmentCreate(d *schema.ResourceData, m interface{}) error { 36 scaleway := m.(*Client).scaleway 37 scaleway.ClearCache() 38 39 vol, err := scaleway.GetVolume(d.Get("volume").(string)) 40 if err != nil { 41 return err 42 } 43 if vol.Server != nil { 44 log.Printf("[DEBUG] Scaleway volume %q already attached to %q.", vol.Identifier, vol.Server.Identifier) 45 return errVolumeAlreadyAttached 46 } 47 48 // guard against server shutdown/ startup race conditiond 49 serverID := d.Get("server").(string) 50 scalewayMutexKV.Lock(serverID) 51 defer scalewayMutexKV.Unlock(serverID) 52 53 server, err := scaleway.GetServer(serverID) 54 if err != nil { 55 fmt.Printf("Failed getting server: %q", err) 56 return err 57 } 58 59 var startServerAgain = false 60 // volumes can only be modified when the server is powered off 61 if server.State != "stopped" { 62 startServerAgain = true 63 64 if err := scaleway.PostServerAction(server.Identifier, "poweroff"); err != nil { 65 return err 66 } 67 } 68 if err := waitForServerState(scaleway, server.Identifier, "stopped"); err != nil { 69 return err 70 } 71 72 volumes := make(map[string]api.ScalewayVolume) 73 for i, volume := range server.Volumes { 74 volumes[i] = volume 75 } 76 77 volumes[fmt.Sprintf("%d", len(volumes)+1)] = *vol 78 79 // the API request requires most volume attributes to be unset to succeed 80 for k, v := range volumes { 81 v.Size = 0 82 v.CreationDate = "" 83 v.Organization = "" 84 v.ModificationDate = "" 85 v.VolumeType = "" 86 v.Server = nil 87 v.ExportURI = "" 88 89 volumes[k] = v 90 } 91 92 if err := resource.Retry(5*time.Minute, func() *resource.RetryError { 93 scaleway.ClearCache() 94 95 var req = api.ScalewayServerPatchDefinition{ 96 Volumes: &volumes, 97 } 98 mu.Lock() 99 err := scaleway.PatchServer(serverID, req) 100 mu.Unlock() 101 102 if err == nil { 103 return nil 104 } 105 106 if serr, ok := err.(api.ScalewayAPIError); ok { 107 log.Printf("[DEBUG] Error patching server: %q\n", serr.APIMessage) 108 109 if serr.StatusCode == 400 { 110 return resource.RetryableError(fmt.Errorf("Waiting for server update to succeed: %q", serr.APIMessage)) 111 } 112 } 113 114 return resource.NonRetryableError(err) 115 }); err != nil { 116 return err 117 } 118 119 if startServerAgain { 120 if err := scaleway.PostServerAction(serverID, "poweron"); err != nil { 121 return err 122 } 123 if err := waitForServerState(scaleway, serverID, "running"); err != nil { 124 return err 125 } 126 } 127 128 d.SetId(fmt.Sprintf("scaleway-server:%s/volume/%s", serverID, d.Get("volume").(string))) 129 130 return resourceScalewayVolumeAttachmentRead(d, m) 131 } 132 133 func resourceScalewayVolumeAttachmentRead(d *schema.ResourceData, m interface{}) error { 134 scaleway := m.(*Client).scaleway 135 scaleway.ClearCache() 136 137 server, err := scaleway.GetServer(d.Get("server").(string)) 138 if err != nil { 139 if serr, ok := err.(api.ScalewayAPIError); ok { 140 log.Printf("[DEBUG] Error reading server: %q\n", serr.APIMessage) 141 142 if serr.StatusCode == 404 { 143 d.SetId("") 144 return nil 145 } 146 } 147 return err 148 } 149 150 if _, err := scaleway.GetVolume(d.Get("volume").(string)); err != nil { 151 if serr, ok := err.(api.ScalewayAPIError); ok { 152 log.Printf("[DEBUG] Error reading volume: %q\n", serr.APIMessage) 153 154 if serr.StatusCode == 404 { 155 d.SetId("") 156 return nil 157 } 158 } 159 return err 160 } 161 162 for _, volume := range server.Volumes { 163 if volume.Identifier == d.Get("volume").(string) { 164 return nil 165 } 166 } 167 168 log.Printf("[DEBUG] Volume %q not attached to server %q\n", d.Get("volume").(string), d.Get("server").(string)) 169 d.SetId("") 170 return nil 171 } 172 173 func resourceScalewayVolumeAttachmentDelete(d *schema.ResourceData, m interface{}) error { 174 scaleway := m.(*Client).scaleway 175 scaleway.ClearCache() 176 177 mu.Lock() 178 defer mu.Unlock() 179 180 var startServerAgain = false 181 182 // guard against server shutdown/ startup race conditiond 183 serverID := d.Get("server").(string) 184 scalewayMutexKV.Lock(serverID) 185 defer scalewayMutexKV.Unlock(serverID) 186 187 server, err := scaleway.GetServer(serverID) 188 if err != nil { 189 return err 190 } 191 192 // volumes can only be modified when the server is powered off 193 if server.State != "stopped" { 194 startServerAgain = true 195 if err := scaleway.PostServerAction(server.Identifier, "poweroff"); err != nil { 196 return err 197 } 198 } 199 if err := waitForServerState(scaleway, server.Identifier, "stopped"); err != nil { 200 return err 201 } 202 203 volumes := make(map[string]api.ScalewayVolume) 204 for _, volume := range server.Volumes { 205 if volume.Identifier != d.Get("volume").(string) { 206 volumes[fmt.Sprintf("%d", len(volumes))] = volume 207 } 208 } 209 210 // the API request requires most volume attributes to be unset to succeed 211 for k, v := range volumes { 212 v.Size = 0 213 v.CreationDate = "" 214 v.Organization = "" 215 v.ModificationDate = "" 216 v.VolumeType = "" 217 v.Server = nil 218 v.ExportURI = "" 219 220 volumes[k] = v 221 } 222 223 if err := resource.Retry(5*time.Minute, func() *resource.RetryError { 224 scaleway.ClearCache() 225 226 var req = api.ScalewayServerPatchDefinition{ 227 Volumes: &volumes, 228 } 229 mu.Lock() 230 err := scaleway.PatchServer(serverID, req) 231 mu.Unlock() 232 233 if err == nil { 234 return nil 235 } 236 237 if serr, ok := err.(api.ScalewayAPIError); ok { 238 log.Printf("[DEBUG] Error patching server: %q\n", serr.APIMessage) 239 240 if serr.StatusCode == 400 { 241 return resource.RetryableError(fmt.Errorf("Waiting for server update to succeed: %q", serr.APIMessage)) 242 } 243 } 244 245 return resource.NonRetryableError(err) 246 }); err != nil { 247 return err 248 } 249 250 if startServerAgain { 251 if err := scaleway.PostServerAction(serverID, "poweron"); err != nil { 252 return err 253 } 254 if err := waitForServerState(scaleway, serverID, "running"); err != nil { 255 return err 256 } 257 } 258 259 d.SetId("") 260 261 return nil 262 }