github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/gce/google/disk.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package google 5 6 import ( 7 "path" 8 9 "github.com/juju/errors" 10 jujuos "github.com/juju/os" 11 "github.com/juju/os/series" 12 "google.golang.org/api/compute/v1" 13 ) 14 15 // The different types of disk persistence supported by GCE. 16 const ( 17 diskPersistenceTypeScratch = "SCRATCH" 18 diskPersistenceTypePersistent = "PERSISTENT" 19 ) 20 21 type DiskType string 22 23 // The types of disk supported by GCE 24 const ( 25 // persistent 26 DiskPersistentStandard DiskType = "pd-standard" 27 DiskPersistentSSD DiskType = "pd-ssd" 28 // scratch 29 DiskLocalSSD DiskType = "local-ssd" 30 ) 31 32 type DiskMode string 33 34 // The different disk modes supported by GCE. 35 const ( 36 ModeRW DiskMode = "READ_WRITE" 37 ModeRO DiskMode = "READ_ONLY" 38 ) 39 40 type DiskStatus string 41 42 const ( 43 StatusCreating DiskStatus = "CREATING" 44 StatusFailed DiskStatus = "FAILED" 45 StatusReady DiskStatus = "READY" 46 StatusRestoring DiskStatus = "RESTORING" 47 ) 48 49 // MinDiskSizeGB is the minimum/default size (in megabytes) for 50 // GCE disks. 51 // 52 // Note: GCE does not currently have an official minimum disk size. 53 // However, in testing we found the minimum size to be 10 GB for ubuntu 54 // and 50 GB for windows due to the image size. See gceapi message. 55 // 56 // gceapi: Requested disk size cannot be smaller than the image size (10 GB) 57 func MinDiskSizeGB(ser string) uint64 { 58 // See comment below that explains why we're ignoring the error 59 os, _ := series.GetOSFromSeries(ser) 60 switch os { 61 case jujuos.Ubuntu: 62 return 10 63 case jujuos.Windows: 64 return 50 65 // On default we just return a "sane" default since the error 66 // will be propagated through the api and appear in juju status anyway 67 default: 68 return 10 69 } 70 } 71 72 // gibToMib converts gibibytes to mebibytes. 73 func gibToMib(g int64) uint64 { 74 return uint64(g) * 1024 75 } 76 77 // DiskSpec holds all the data needed to request a new disk on GCE. 78 // Some fields are used only for attached disks (i.e. in association 79 // with instances). 80 type DiskSpec struct { 81 // Series is the OS series on which the disk size depends 82 Series string 83 // SizeHintGB is the requested disk size in Gigabytes. It must be 84 // greater than 0. 85 SizeHintGB uint64 86 // ImageURL is the location of the image to which the disk should 87 // be initialized. 88 ImageURL string 89 // Boot indicates that this is a boot disk. An instance may only 90 // have one boot disk. (attached only) 91 Boot bool 92 // Scratch indicates that the disk should be a "scratch" disk 93 // instead of a "persistent" disk (the default). 94 Scratch bool 95 // Readonly indicates that the disk should not support writes. 96 Readonly bool 97 // AutoDelete indicates that the attached disk should be removed 98 // when the instance to which it is attached is removed. 99 AutoDelete bool 100 // PersistenDiskType is exclusive to persistent disks and indicates which of the 101 // persistent types available this disk should be. 102 PersistentDiskType DiskType 103 // Name: Name of the resource; provided by the client when the resource 104 // is created. The name must be 1-63 characters long, and comply with 105 // RFC1035. Specifically, the name must be 1-63 characters long and 106 // match the regular expression [a-z]([-a-z0-9]*[a-z0-9])? which means 107 // the first character must be a lowercase letter, and all following 108 // characters must be a dash, lowercase letter, or digit, except the 109 // last character, which cannot be a dash. 110 Name string 111 // Labels holds labels/metadata for the disk. Labels are used for 112 // storing volume resource tags. 113 Labels map[string]string 114 } 115 116 // TooSmall checks the spec's size hint and indicates whether or not 117 // it is smaller than the minimum disk size. 118 func (ds *DiskSpec) TooSmall() bool { 119 return ds.SizeHintGB < MinDiskSizeGB(ds.Series) 120 } 121 122 // SizeGB returns the disk size to use for a new disk. The size hint 123 // is returned if it isn't too small (otherwise the min size is 124 // returned). 125 func (ds *DiskSpec) SizeGB() uint64 { 126 size := ds.SizeHintGB 127 if ds.TooSmall() { 128 size = MinDiskSizeGB(ds.Series) 129 } 130 return size 131 } 132 133 // newAttached builds a compute.AttachedDisk using the information in 134 // the disk spec and returns it. 135 // 136 // Note: Not all AttachedDisk fields are set. 137 func (ds *DiskSpec) newAttached() *compute.AttachedDisk { 138 // TODO(ericsnow) Fail if SizeHintGB is 0? 139 diskType := diskPersistenceTypePersistent 140 if ds.Scratch { 141 diskType = diskPersistenceTypeScratch 142 } 143 mode := ModeRW 144 if ds.Readonly { 145 mode = ModeRO 146 } 147 148 disk := compute.AttachedDisk{ 149 Type: diskType, 150 Boot: ds.Boot, 151 Mode: string(mode), 152 AutoDelete: ds.AutoDelete, 153 InitializeParams: &compute.AttachedDiskInitializeParams{ 154 // DiskName (defaults to instance name) 155 DiskSizeGb: int64(ds.SizeGB()), 156 // DiskType (defaults to pd-standard, pd-ssd, local-ssd) 157 SourceImage: ds.ImageURL, 158 }, 159 // Interface (defaults to SCSI) 160 // DeviceName (GCE sets this, persistent disk only) 161 } 162 return &disk 163 } 164 165 // newDetached creates a new detached persistent disk representation, 166 // this DOES NOT create a disk in gce, just creates the spec. 167 // reference in https://cloud.google.com/compute/docs/reference/latest/disks#resource 168 func (ds *DiskSpec) newDetached() (*compute.Disk, error) { 169 if ds.Scratch { 170 return nil, errors.New("cannot create scratch volumes detached") 171 } 172 if ds.PersistentDiskType == DiskLocalSSD { 173 return nil, errors.New("cannot create local ssd disks detached") 174 } 175 return &compute.Disk{ 176 Name: ds.Name, 177 SizeGb: int64(ds.SizeGB()), 178 SourceImage: ds.ImageURL, 179 Type: string(ds.PersistentDiskType), 180 Labels: ds.Labels, 181 }, nil 182 } 183 184 // AttachedDisk represents a disk that is attached to an instance. 185 type AttachedDisk struct { 186 // VolumeName is the name of the volume that is attached, this is unique 187 // and used by gce as an identifier. 188 VolumeName string 189 // DeviceName is the name of the device in the instance, typycally 190 // is reflected into the /dev/disk/by-id/google-* 191 DeviceName string 192 // Mode is the read/write mode of the disk. 193 Mode DiskMode 194 } 195 196 // Disk represents a gce disk. 197 type Disk struct { 198 // Id is an unique identifier google adds to the disk, it usually 199 // is not used in the API. 200 Id uint64 201 202 // Name is a unique identifier string for each disk. 203 Name string 204 205 // Description holds the description field for a disk, we used to 206 // store the model UUID here. 207 Description string 208 209 // Size is the size in mbit. 210 Size uint64 211 212 // Type is one of the available disk types supported by 213 // gce (persistent or ephemeral). 214 Type DiskType 215 216 // Zone holds the name of the zone in which the disk lives. 217 Zone string 218 219 // DiskStatus holds the status of he aforementioned disk. 220 Status DiskStatus 221 222 // AttachedInstances holds the IDs of instances that have the disk 223 // attached. 224 AttachedInstances []string 225 226 // Labels holds labels/metadata for the disk. Labels are used for 227 // storing volume resource tags. 228 Labels map[string]string 229 230 // LabelFingerprint holds a hash of the labels, to be used to prevent 231 // conflicting changes to labels. 232 LabelFingerprint string 233 } 234 235 func NewDisk(cd *compute.Disk) *Disk { 236 // cd.Users contains the users of the disk (attached instances), 237 // in form: project/zones/zone/instances/instance. Disks and 238 // instances must be in the same zone, so we just take the 239 // final part of the path. 240 attachedInstances := make([]string, len(cd.Users)) 241 for i, u := range cd.Users { 242 attachedInstances[i] = path.Base(u) 243 } 244 d := &Disk{ 245 Id: cd.Id, 246 Name: cd.Name, 247 Description: cd.Description, 248 Size: gibToMib(cd.SizeGb), 249 Type: DiskType(cd.Type), 250 Zone: path.Base(cd.Zone), 251 Status: DiskStatus(cd.Status), 252 Labels: cd.Labels, 253 LabelFingerprint: cd.LabelFingerprint, 254 AttachedInstances: attachedInstances, 255 } 256 return d 257 }