github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/gce/google/conn_disks.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package google 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/juju/errors" 11 "google.golang.org/api/compute/v1" 12 ) 13 14 // CreateDisks implements storage section of gceConnection. 15 func (gce *Connection) CreateDisks(zone string, disks []DiskSpec) ([]*Disk, error) { 16 results := make([]*Disk, len(disks)) 17 for i, disk := range disks { 18 d, err := disk.newDetached() 19 if err != nil { 20 return []*Disk{}, errors.Annotate(err, "cannot create disk spec") 21 } 22 if err := gce.createDisk(zone, d); err != nil { 23 return []*Disk{}, errors.Annotatef(err, "cannot create disk %q", disk.Name) 24 } 25 results[i] = NewDisk(d) 26 } 27 return results, nil 28 } 29 30 func (gce *Connection) createDisk(zone string, disk *compute.Disk) error { 31 return gce.raw.CreateDisk(gce.projectID, zone, disk) 32 } 33 34 // Disks implements storage section of gceConnection. 35 func (gce *Connection) Disks() ([]*Disk, error) { 36 computeDisks, err := gce.raw.ListDisks(gce.projectID) 37 if err != nil { 38 return nil, errors.Annotate(err, "cannot list disks") 39 } 40 disks := make([]*Disk, len(computeDisks)) 41 for i, disk := range computeDisks { 42 disks[i] = NewDisk(disk) 43 } 44 return disks, nil 45 } 46 47 // RemoveDisk implements storage section of gceConnection. 48 // TODO(perrito666) handle non existing disk, perhaps catch 404. 49 func (gce *Connection) RemoveDisk(zone, name string) error { 50 return gce.raw.RemoveDisk(gce.projectID, zone, name) 51 } 52 53 // Disk implements storage section of gceConnection. 54 func (gce *Connection) Disk(zone, name string) (*Disk, error) { 55 d, err := gce.raw.GetDisk(gce.projectID, zone, name) 56 if err != nil { 57 return nil, errors.Annotatef(err, "cannot get disk %q in zone %q", name, zone) 58 } 59 return NewDisk(d), nil 60 } 61 62 // SetDiskLabels implements storage section of gceConnection. 63 func (gce *Connection) SetDiskLabels(zone, name, labelFingerprint string, labels map[string]string) error { 64 err := gce.raw.SetDiskLabels(gce.projectID, zone, name, labelFingerprint, labels) 65 return errors.Annotatef(err, "cannot update labels for disk %q in zone %q", name, zone) 66 } 67 68 // deviceName will generate a device name from the passed 69 // <zone> and <diskId>, the device name must not be confused 70 // with the volume name, as it is used mainly to name the 71 // disk when attached to a linux OS. 72 func deviceName(zone string, diskId uint64) string { 73 return fmt.Sprintf("%s-%d", zone, diskId) 74 } 75 76 // AttachDisk implements storage section of gceConnection. 77 func (gce *Connection) AttachDisk(zone, volumeName, instanceId string, mode DiskMode) (*AttachedDisk, error) { 78 disk, err := gce.raw.GetDisk(gce.projectID, zone, volumeName) 79 if err != nil { 80 return nil, errors.Annotatef(err, "cannot obtain disk %q to attach it", volumeName) 81 } 82 attachedDisk := &compute.AttachedDisk{ 83 // Specifies a unique device name of your choice that 84 // is reflected into the /dev/disk/by-id/google-* 85 DeviceName: deviceName(zone, disk.Id), 86 Source: disk.SelfLink, 87 Mode: string(mode), 88 } 89 err = gce.raw.AttachDisk(gce.projectID, zone, instanceId, attachedDisk) 90 if err != nil { 91 return nil, errors.Annotate(err, "cannot attach disk") 92 } 93 return &AttachedDisk{ 94 VolumeName: volumeName, 95 DeviceName: attachedDisk.DeviceName, 96 Mode: mode, 97 }, nil 98 } 99 100 // DetachDisk implements storage section of gceConnection. 101 // disk existence is checked but not instance nor is attachment. 102 func (gce *Connection) DetachDisk(zone, instanceId, volumeName string) error { 103 disk, err := gce.raw.GetDisk(gce.projectID, zone, volumeName) 104 if err != nil { 105 return errors.Annotatef(err, "cannot obtain disk %q to detach it", volumeName) 106 } 107 dn := deviceName(zone, disk.Id) 108 err = gce.raw.DetachDisk(gce.projectID, zone, instanceId, dn) 109 if err != nil { 110 return errors.Annotatef(err, "cannot detach %q from %q", dn, instanceId) 111 } 112 return nil 113 } 114 115 // sourceToVolumeName will return the disk Name part of a 116 // source URL for a compute disk, compute is a bit inconsistent 117 // on its handling of disk resources, when used in requests it will 118 // take the disk.Name but when used as a parameter it will take 119 // the source url. 120 // The source url (short) format is: 121 // /projects/project/zones/zone/disks/disk 122 // the relevant part is disk. 123 func sourceToVolumeName(source string) string { 124 if source == "" { 125 return "" 126 } 127 parts := strings.Split(source, "/") 128 if len(parts) == 1 { 129 return source 130 } 131 lastItem := len(parts) - 1 132 return parts[lastItem] 133 } 134 135 // InstanceDisks implements storage section of gceConnection. 136 func (gce *Connection) InstanceDisks(zone, instanceId string) ([]*AttachedDisk, error) { 137 disks, err := gce.raw.InstanceDisks(gce.projectID, zone, instanceId) 138 if err != nil { 139 return nil, errors.Annotatef(err, "cannot get disks from instance") 140 } 141 att := make([]*AttachedDisk, len(disks)) 142 for i, disk := range disks { 143 att[i] = &AttachedDisk{ 144 VolumeName: sourceToVolumeName(disk.Source), 145 DeviceName: disk.DeviceName, 146 Mode: DiskMode(disk.Mode), 147 } 148 } 149 return att, nil 150 }