github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/gce/google/instance.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 "fmt" 8 "path" 9 10 "google.golang.org/api/compute/v1" 11 12 "github.com/juju/juju/network" 13 ) 14 15 // InstanceSpec holds all the information needed to create a new GCE 16 // instance within some zone. 17 // TODO(ericsnow) Validate the invariants? 18 type InstanceSpec struct { 19 // ID is the "name" of the instance. 20 ID string 21 // Type is the name of the GCE instance type. The value is resolved 22 // relative to an availability zone when the API request is sent. 23 // The type must match one of the GCE-recognized types. 24 Type string 25 // Disks holds the information needed to request each of the disks 26 // that should be attached to a new instance. This must include a 27 // single root disk. 28 Disks []DiskSpec 29 // Network identifies the information for the network that a new 30 // instance should use. If the network does not exist then it will 31 // be added when the instance is. At least the network's name must 32 // be set. 33 Network NetworkSpec 34 // NetworkInterfaces is the names of the network interfaces to 35 // associate with the instance. They will be connected to the the 36 // network identified by the instance spec. At least one name must 37 // be provided. 38 NetworkInterfaces []string 39 // Metadata is the GCE instance "user-specified" metadata that will 40 // be initialized on the new instance. 41 Metadata map[string]string 42 // Tags are the labels to associate with the instance. This is 43 // useful when making bulk calls or in relation to some API methods 44 // (e.g. related to firewalls access rules). 45 Tags []string 46 } 47 48 func (is InstanceSpec) raw() *compute.Instance { 49 return &compute.Instance{ 50 Name: is.ID, 51 Disks: is.disks(), 52 NetworkInterfaces: is.networkInterfaces(), 53 Metadata: packMetadata(is.Metadata), 54 Tags: &compute.Tags{Items: is.Tags}, 55 // MachineType is set in the addInstance call. 56 } 57 } 58 59 // Summary builds an InstanceSummary based on the spec and returns it. 60 func (is InstanceSpec) Summary() InstanceSummary { 61 raw := is.raw() 62 return newInstanceSummary(raw) 63 } 64 65 func (is InstanceSpec) disks() []*compute.AttachedDisk { 66 var result []*compute.AttachedDisk 67 for _, spec := range is.Disks { 68 result = append(result, spec.newAttached()) 69 } 70 return result 71 } 72 73 func (is InstanceSpec) networkInterfaces() []*compute.NetworkInterface { 74 var result []*compute.NetworkInterface 75 for _, name := range is.NetworkInterfaces { 76 result = append(result, is.Network.newInterface(name)) 77 } 78 return result 79 } 80 81 // RootDisk identifies the root disk for a given instance (or instance 82 // spec) and returns it. If the root disk could not be determined then 83 // nil is returned. 84 // TODO(ericsnow) Return an error? 85 func (is InstanceSpec) RootDisk() *compute.AttachedDisk { 86 return is.Disks[0].newAttached() 87 } 88 89 // InstanceSummary captures all the data needed by Instance. 90 type InstanceSummary struct { 91 // ID is the "name" of the instance. 92 ID string 93 // ZoneName is the unqualified name of the zone in which the 94 // instance was provisioned. 95 ZoneName string 96 // Status holds the status of the instance at a certain point in time. 97 Status string 98 // Metadata is the instance metadata. 99 Metadata map[string]string 100 // Addresses are the IP Addresses associated with the instance. 101 Addresses []network.Address 102 } 103 104 func newInstanceSummary(raw *compute.Instance) InstanceSummary { 105 return InstanceSummary{ 106 ID: raw.Name, 107 ZoneName: path.Base(raw.Zone), 108 Status: raw.Status, 109 Metadata: unpackMetadata(raw.Metadata), 110 Addresses: extractAddresses(raw.NetworkInterfaces...), 111 } 112 } 113 114 // Instance represents a single realized GCE compute instance. 115 type Instance struct { 116 InstanceSummary 117 118 // spec is the InstanceSpec used to create this instance. 119 spec *InstanceSpec 120 } 121 122 func newInstance(raw *compute.Instance, spec *InstanceSpec) *Instance { 123 summary := newInstanceSummary(raw) 124 return NewInstance(summary, spec) 125 } 126 127 // NewInstance builds an instance from the provided summary and spec 128 // and returns it. 129 func NewInstance(summary InstanceSummary, spec *InstanceSpec) *Instance { 130 if spec != nil { 131 // Make a copy. 132 val := *spec 133 spec = &val 134 } 135 return &Instance{ 136 InstanceSummary: summary, 137 spec: spec, 138 } 139 } 140 141 // RootDisk returns an AttachedDisk 142 func (gi Instance) RootDisk() *compute.AttachedDisk { 143 if gi.spec == nil { 144 return nil 145 } 146 return gi.spec.RootDisk() 147 } 148 149 // RootDiskGB returns the size of the instance's root disk. If it 150 // cannot be determined then 0 is returned. 151 func (gi Instance) RootDiskGB() uint64 { 152 if gi.spec == nil { 153 return 0 154 } 155 attached := gi.RootDisk() 156 return uint64(attached.InitializeParams.DiskSizeGb) 157 } 158 159 // Status returns a string identifying the status of the instance. The 160 // value will match one of the Status* constants in the package. 161 func (gi Instance) Status() string { 162 return gi.InstanceSummary.Status 163 } 164 165 // Addresses identifies information about the network addresses 166 // associated with the instance and returns it. 167 func (gi Instance) Addresses() []network.Address { 168 // TODO*ericsnow) return a copy? 169 return gi.InstanceSummary.Addresses 170 } 171 172 // Metadata returns the user-specified metadata for the instance. 173 func (gi Instance) Metadata() map[string]string { 174 // TODO*ericsnow) return a copy? 175 return gi.InstanceSummary.Metadata 176 } 177 178 // packMetadata composes the provided data into the format required 179 // by the GCE API. 180 func packMetadata(data map[string]string) *compute.Metadata { 181 var items []*compute.MetadataItems 182 for key, value := range data { 183 item := compute.MetadataItems{ 184 Key: key, 185 Value: value, 186 } 187 items = append(items, &item) 188 } 189 return &compute.Metadata{Items: items} 190 } 191 192 // unpackMetadata decomposes the provided data from the format used 193 // in the GCE API. 194 func unpackMetadata(data *compute.Metadata) map[string]string { 195 if data == nil { 196 return nil 197 } 198 199 result := make(map[string]string) 200 for _, item := range data.Items { 201 result[item.Key] = item.Value 202 } 203 return result 204 } 205 206 func formatMachineType(zone, name string) string { 207 return fmt.Sprintf("zones/%s/machineTypes/%s", zone, name) 208 }