github.com/bradfeehan/terraform@v0.7.0-rc3.0.20170529055808-34b45c5ad841/builtin/providers/google/resource_compute_disk.go (about) 1 package google 2 3 import ( 4 "fmt" 5 "log" 6 "regexp" 7 8 "github.com/hashicorp/terraform/helper/schema" 9 "google.golang.org/api/compute/v1" 10 "google.golang.org/api/googleapi" 11 ) 12 13 const ( 14 computeDiskUserRegexString = "^(?:https://www.googleapis.com/compute/v1/projects/)?([-_a-zA-Z0-9]*)/zones/([-_a-zA-Z0-9]*)/instances/([-_a-zA-Z0-9]*)$" 15 ) 16 17 var ( 18 computeDiskUserRegex = regexp.MustCompile(computeDiskUserRegexString) 19 ) 20 21 func resourceComputeDisk() *schema.Resource { 22 return &schema.Resource{ 23 Create: resourceComputeDiskCreate, 24 Read: resourceComputeDiskRead, 25 Delete: resourceComputeDiskDelete, 26 27 Schema: map[string]*schema.Schema{ 28 "name": &schema.Schema{ 29 Type: schema.TypeString, 30 Required: true, 31 ForceNew: true, 32 }, 33 34 "zone": &schema.Schema{ 35 Type: schema.TypeString, 36 Required: true, 37 ForceNew: true, 38 }, 39 40 "disk_encryption_key_raw": &schema.Schema{ 41 Type: schema.TypeString, 42 Optional: true, 43 ForceNew: true, 44 Sensitive: true, 45 }, 46 47 "disk_encryption_key_sha256": &schema.Schema{ 48 Type: schema.TypeString, 49 Computed: true, 50 }, 51 52 "image": &schema.Schema{ 53 Type: schema.TypeString, 54 Optional: true, 55 ForceNew: true, 56 }, 57 58 "project": &schema.Schema{ 59 Type: schema.TypeString, 60 Optional: true, 61 ForceNew: true, 62 }, 63 64 "size": &schema.Schema{ 65 Type: schema.TypeInt, 66 Optional: true, 67 ForceNew: true, 68 }, 69 70 "self_link": &schema.Schema{ 71 Type: schema.TypeString, 72 Computed: true, 73 }, 74 75 "snapshot": &schema.Schema{ 76 Type: schema.TypeString, 77 Optional: true, 78 ForceNew: true, 79 }, 80 81 "type": &schema.Schema{ 82 Type: schema.TypeString, 83 Optional: true, 84 ForceNew: true, 85 }, 86 "users": &schema.Schema{ 87 Type: schema.TypeList, 88 Computed: true, 89 Elem: &schema.Schema{Type: schema.TypeString}, 90 }, 91 }, 92 } 93 } 94 95 func resourceComputeDiskCreate(d *schema.ResourceData, meta interface{}) error { 96 config := meta.(*Config) 97 98 project, err := getProject(d, config) 99 if err != nil { 100 return err 101 } 102 103 // Get the zone 104 log.Printf("[DEBUG] Loading zone: %s", d.Get("zone").(string)) 105 zone, err := config.clientCompute.Zones.Get( 106 project, d.Get("zone").(string)).Do() 107 if err != nil { 108 return fmt.Errorf( 109 "Error loading zone '%s': %s", d.Get("zone").(string), err) 110 } 111 112 // Build the disk parameter 113 disk := &compute.Disk{ 114 Name: d.Get("name").(string), 115 SizeGb: int64(d.Get("size").(int)), 116 } 117 118 // If we were given a source image, load that. 119 if v, ok := d.GetOk("image"); ok { 120 log.Printf("[DEBUG] Resolving image name: %s", v.(string)) 121 imageUrl, err := resolveImage(config, v.(string)) 122 if err != nil { 123 return fmt.Errorf( 124 "Error resolving image name '%s': %s", 125 v.(string), err) 126 } 127 128 disk.SourceImage = imageUrl 129 log.Printf("[DEBUG] Image name resolved to: %s", imageUrl) 130 } 131 132 if v, ok := d.GetOk("type"); ok { 133 log.Printf("[DEBUG] Loading disk type: %s", v.(string)) 134 diskType, err := readDiskType(config, zone, v.(string)) 135 if err != nil { 136 return fmt.Errorf( 137 "Error loading disk type '%s': %s", 138 v.(string), err) 139 } 140 141 disk.Type = diskType.SelfLink 142 } 143 144 if v, ok := d.GetOk("snapshot"); ok { 145 snapshotName := v.(string) 146 match, _ := regexp.MatchString("^https://www.googleapis.com/compute", snapshotName) 147 if match { 148 disk.SourceSnapshot = snapshotName 149 } else { 150 log.Printf("[DEBUG] Loading snapshot: %s", snapshotName) 151 snapshotData, err := config.clientCompute.Snapshots.Get( 152 project, snapshotName).Do() 153 154 if err != nil { 155 return fmt.Errorf( 156 "Error loading snapshot '%s': %s", 157 snapshotName, err) 158 } 159 disk.SourceSnapshot = snapshotData.SelfLink 160 } 161 } 162 163 if v, ok := d.GetOk("disk_encryption_key_raw"); ok { 164 disk.DiskEncryptionKey = &compute.CustomerEncryptionKey{} 165 disk.DiskEncryptionKey.RawKey = v.(string) 166 } 167 168 op, err := config.clientCompute.Disks.Insert( 169 project, d.Get("zone").(string), disk).Do() 170 if err != nil { 171 return fmt.Errorf("Error creating disk: %s", err) 172 } 173 174 // It probably maybe worked, so store the ID now 175 d.SetId(disk.Name) 176 177 err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Creating Disk") 178 if err != nil { 179 return err 180 } 181 return resourceComputeDiskRead(d, meta) 182 } 183 184 func resourceComputeDiskRead(d *schema.ResourceData, meta interface{}) error { 185 config := meta.(*Config) 186 187 project, err := getProject(d, config) 188 if err != nil { 189 return err 190 } 191 192 disk, err := config.clientCompute.Disks.Get( 193 project, d.Get("zone").(string), d.Id()).Do() 194 if err != nil { 195 return handleNotFoundError(err, d, fmt.Sprintf("Disk %q", d.Get("name").(string))) 196 } 197 198 d.Set("self_link", disk.SelfLink) 199 if disk.DiskEncryptionKey != nil && disk.DiskEncryptionKey.Sha256 != "" { 200 d.Set("disk_encryption_key_sha256", disk.DiskEncryptionKey.Sha256) 201 } 202 d.Set("users", disk.Users) 203 204 return nil 205 } 206 207 func resourceComputeDiskDelete(d *schema.ResourceData, meta interface{}) error { 208 config := meta.(*Config) 209 210 project, err := getProject(d, config) 211 if err != nil { 212 return err 213 } 214 215 // if disks are attached, they must be detached before the disk can be deleted 216 if instances, ok := d.Get("users").([]interface{}); ok { 217 type detachArgs struct{ project, zone, instance, deviceName string } 218 var detachCalls []detachArgs 219 self := d.Get("self_link").(string) 220 for _, instance := range instances { 221 if !computeDiskUserRegex.MatchString(instance.(string)) { 222 return fmt.Errorf("Unknown user %q of disk %q", instance, self) 223 } 224 matches := computeDiskUserRegex.FindStringSubmatch(instance.(string)) 225 instanceProject := matches[1] 226 instanceZone := matches[2] 227 instanceName := matches[3] 228 i, err := config.clientCompute.Instances.Get(instanceProject, instanceZone, instanceName).Do() 229 if err != nil { 230 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 231 log.Printf("[WARN] instance %q not found, not bothering to detach disks", instance.(string)) 232 continue 233 } 234 return fmt.Errorf("Error retrieving instance %s: %s", instance.(string), err.Error()) 235 } 236 for _, disk := range i.Disks { 237 if disk.Source == self { 238 detachCalls = append(detachCalls, detachArgs{ 239 project: project, 240 zone: i.Zone, 241 instance: i.Name, 242 deviceName: disk.DeviceName, 243 }) 244 } 245 } 246 } 247 for _, call := range detachCalls { 248 op, err := config.clientCompute.Instances.DetachDisk(call.project, call.zone, call.instance, call.deviceName).Do() 249 if err != nil { 250 return fmt.Errorf("Error detaching disk %s from instance %s/%s/%s: %s", call.deviceName, call.project, 251 call.zone, call.instance, err.Error()) 252 } 253 err = computeOperationWaitZone(config, op, call.project, call.zone, 254 fmt.Sprintf("Detaching disk from %s/%s/%s", call.project, call.zone, call.instance)) 255 if err != nil { 256 return err 257 } 258 } 259 } 260 261 // Delete the disk 262 op, err := config.clientCompute.Disks.Delete( 263 project, d.Get("zone").(string), d.Id()).Do() 264 if err != nil { 265 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 266 log.Printf("[WARN] Removing Disk %q because it's gone", d.Get("name").(string)) 267 // The resource doesn't exist anymore 268 d.SetId("") 269 return nil 270 } 271 return fmt.Errorf("Error deleting disk: %s", err) 272 } 273 274 zone := d.Get("zone").(string) 275 err = computeOperationWaitZone(config, op, project, zone, "Deleting Disk") 276 if err != nil { 277 return err 278 } 279 280 d.SetId("") 281 return nil 282 }