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