github.com/richardbowden/terraform@v0.6.12-0.20160901200758-30ea22c25211/builtin/providers/packet/resource_packet_volume.go (about) 1 package packet 2 3 import ( 4 "errors" 5 "fmt" 6 "time" 7 8 "github.com/hashicorp/terraform/helper/resource" 9 "github.com/hashicorp/terraform/helper/schema" 10 "github.com/packethost/packngo" 11 ) 12 13 func resourcePacketVolume() *schema.Resource { 14 return &schema.Resource{ 15 Create: resourcePacketVolumeCreate, 16 Read: resourcePacketVolumeRead, 17 Update: resourcePacketVolumeUpdate, 18 Delete: resourcePacketVolumeDelete, 19 20 Schema: map[string]*schema.Schema{ 21 "id": &schema.Schema{ 22 Type: schema.TypeString, 23 Computed: true, 24 }, 25 26 "project_id": &schema.Schema{ 27 Type: schema.TypeString, 28 Required: true, 29 ForceNew: true, 30 }, 31 32 "name": &schema.Schema{ 33 Type: schema.TypeString, 34 Computed: true, 35 }, 36 37 "description": &schema.Schema{ 38 Type: schema.TypeString, 39 Required: false, 40 Optional: true, 41 }, 42 43 "size": &schema.Schema{ 44 Type: schema.TypeInt, 45 Required: true, 46 }, 47 48 "facility": &schema.Schema{ 49 Type: schema.TypeString, 50 Required: true, 51 ForceNew: true, 52 }, 53 54 "plan": &schema.Schema{ 55 Type: schema.TypeString, 56 Required: true, 57 }, 58 59 "billing_cycle": &schema.Schema{ 60 Type: schema.TypeString, 61 Computed: true, 62 Optional: true, 63 }, 64 65 "state": &schema.Schema{ 66 Type: schema.TypeString, 67 Computed: true, 68 }, 69 70 "locked": &schema.Schema{ 71 Type: schema.TypeBool, 72 Optional: true, 73 }, 74 75 "snapshot_policies": &schema.Schema{ 76 Type: schema.TypeList, 77 Optional: true, 78 Elem: &schema.Resource{ 79 Schema: map[string]*schema.Schema{ 80 "snapshot_frequency": &schema.Schema{ 81 Type: schema.TypeString, 82 Required: true, 83 ForceNew: true, 84 }, 85 "snapshot_count": &schema.Schema{ 86 Type: schema.TypeInt, 87 Required: true, 88 ForceNew: true, 89 }, 90 }, 91 }, 92 }, 93 94 "attachments": &schema.Schema{ 95 Type: schema.TypeList, 96 Computed: true, 97 Elem: &schema.Resource{ 98 Schema: map[string]*schema.Schema{ 99 "href": &schema.Schema{ 100 Type: schema.TypeString, 101 Computed: true, 102 }, 103 }, 104 }, 105 }, 106 107 "created": &schema.Schema{ 108 Type: schema.TypeString, 109 Computed: true, 110 }, 111 112 "updated": &schema.Schema{ 113 Type: schema.TypeString, 114 Computed: true, 115 }, 116 }, 117 } 118 } 119 120 func resourcePacketVolumeCreate(d *schema.ResourceData, meta interface{}) error { 121 client := meta.(*packngo.Client) 122 123 createRequest := &packngo.VolumeCreateRequest{ 124 PlanID: d.Get("plan").(string), 125 FacilityID: d.Get("facility").(string), 126 ProjectID: d.Get("project_id").(string), 127 Size: d.Get("size").(int), 128 } 129 130 if attr, ok := d.GetOk("billing_cycle"); ok { 131 createRequest.BillingCycle = attr.(string) 132 } else { 133 createRequest.BillingCycle = "hourly" 134 } 135 136 if attr, ok := d.GetOk("description"); ok { 137 createRequest.Description = attr.(string) 138 } 139 140 snapshot_count := d.Get("snapshot_policies.#").(int) 141 if snapshot_count > 0 { 142 createRequest.SnapshotPolicies = make([]*packngo.SnapshotPolicy, 0, snapshot_count) 143 for i := 0; i < snapshot_count; i++ { 144 policy := new(packngo.SnapshotPolicy) 145 policy.SnapshotFrequency = d.Get(fmt.Sprintf("snapshot_policies.%d.snapshot_frequency", i)).(string) 146 policy.SnapshotCount = d.Get(fmt.Sprintf("snapshot_policies.%d.snapshot_count", i)).(int) 147 createRequest.SnapshotPolicies = append(createRequest.SnapshotPolicies, policy) 148 } 149 } 150 151 newVolume, _, err := client.Volumes.Create(createRequest) 152 if err != nil { 153 return friendlyError(err) 154 } 155 156 d.SetId(newVolume.ID) 157 158 _, err = waitForVolumeAttribute(d, "active", []string{"queued", "provisioning"}, "state", meta) 159 if err != nil { 160 if isForbidden(err) { 161 // If the volume doesn't get to the active state, we can't recover it from here. 162 d.SetId("") 163 164 return errors.New("provisioning time limit exceeded; the Packet team will investigate") 165 } 166 return err 167 } 168 169 return resourcePacketVolumeRead(d, meta) 170 } 171 172 func waitForVolumeAttribute(d *schema.ResourceData, target string, pending []string, attribute string, meta interface{}) (interface{}, error) { 173 stateConf := &resource.StateChangeConf{ 174 Pending: pending, 175 Target: []string{target}, 176 Refresh: newVolumeStateRefreshFunc(d, attribute, meta), 177 Timeout: 60 * time.Minute, 178 Delay: 10 * time.Second, 179 MinTimeout: 3 * time.Second, 180 } 181 return stateConf.WaitForState() 182 } 183 184 func newVolumeStateRefreshFunc(d *schema.ResourceData, attribute string, meta interface{}) resource.StateRefreshFunc { 185 client := meta.(*packngo.Client) 186 187 return func() (interface{}, string, error) { 188 if err := resourcePacketVolumeRead(d, meta); err != nil { 189 return nil, "", err 190 } 191 192 if attr, ok := d.GetOk(attribute); ok { 193 volume, _, err := client.Volumes.Get(d.Id()) 194 if err != nil { 195 return nil, "", friendlyError(err) 196 } 197 return &volume, attr.(string), nil 198 } 199 200 return nil, "", nil 201 } 202 } 203 204 func resourcePacketVolumeRead(d *schema.ResourceData, meta interface{}) error { 205 client := meta.(*packngo.Client) 206 207 volume, _, err := client.Volumes.Get(d.Id()) 208 if err != nil { 209 err = friendlyError(err) 210 211 // If the volume somehow already destroyed, mark as succesfully gone. 212 if isNotFound(err) { 213 d.SetId("") 214 return nil 215 } 216 217 return err 218 } 219 220 d.Set("name", volume.Name) 221 d.Set("description", volume.Description) 222 d.Set("size", volume.Size) 223 d.Set("plan", volume.Plan.Slug) 224 d.Set("facility", volume.Facility.Code) 225 d.Set("state", volume.State) 226 d.Set("billing_cycle", volume.BillingCycle) 227 d.Set("locked", volume.Locked) 228 d.Set("created", volume.Created) 229 d.Set("updated", volume.Updated) 230 231 snapshot_policies := make([]map[string]interface{}, 0, len(volume.SnapshotPolicies)) 232 for _, snapshot_policy := range volume.SnapshotPolicies { 233 policy := map[string]interface{}{ 234 "snapshot_frequency": snapshot_policy.SnapshotFrequency, 235 "snapshot_count": snapshot_policy.SnapshotCount, 236 } 237 snapshot_policies = append(snapshot_policies, policy) 238 } 239 d.Set("snapshot_policies", snapshot_policies) 240 241 attachments := make([]*packngo.Attachment, 0, len(volume.Attachments)) 242 for _, attachment := range volume.Attachments { 243 attachments = append(attachments, attachment) 244 } 245 d.Set("attachments", attachments) 246 247 return nil 248 } 249 250 func resourcePacketVolumeUpdate(d *schema.ResourceData, meta interface{}) error { 251 client := meta.(*packngo.Client) 252 253 updateRequest := &packngo.VolumeUpdateRequest{ 254 ID: d.Get("id").(string), 255 } 256 257 if attr, ok := d.GetOk("description"); ok { 258 updateRequest.Description = attr.(string) 259 } 260 261 if attr, ok := d.GetOk("plan"); ok { 262 updateRequest.Plan = attr.(string) 263 } 264 265 _, _, err := client.Volumes.Update(updateRequest) 266 if err != nil { 267 return friendlyError(err) 268 } 269 270 return resourcePacketVolumeRead(d, meta) 271 } 272 273 func resourcePacketVolumeDelete(d *schema.ResourceData, meta interface{}) error { 274 client := meta.(*packngo.Client) 275 276 if _, err := client.Volumes.Delete(d.Id()); err != nil { 277 return friendlyError(err) 278 } 279 280 return nil 281 }