github.com/homburg/packer@v0.6.1-0.20140528012651-1dcaf1716848/builder/googlecompute/driver_gce.go (about) 1 package googlecompute 2 3 import ( 4 "fmt" 5 "log" 6 "net/http" 7 "time" 8 9 "code.google.com/p/goauth2/oauth" 10 "code.google.com/p/goauth2/oauth/jwt" 11 "code.google.com/p/google-api-go-client/compute/v1" 12 "github.com/mitchellh/packer/packer" 13 ) 14 15 // driverGCE is a Driver implementation that actually talks to GCE. 16 // Create an instance using NewDriverGCE. 17 type driverGCE struct { 18 projectId string 19 service *compute.Service 20 ui packer.Ui 21 } 22 23 const DriverScopes string = "https://www.googleapis.com/auth/compute " + 24 "https://www.googleapis.com/auth/devstorage.full_control" 25 26 func NewDriverGCE(ui packer.Ui, projectId string, c *clientSecrets, key []byte) (Driver, error) { 27 log.Printf("[INFO] Requesting token...") 28 log.Printf("[INFO] -- Email: %s", c.Web.ClientEmail) 29 log.Printf("[INFO] -- Scopes: %s", DriverScopes) 30 log.Printf("[INFO] -- Private Key Length: %d", len(key)) 31 log.Printf("[INFO] -- Token URL: %s", c.Web.TokenURI) 32 jwtTok := jwt.NewToken(c.Web.ClientEmail, DriverScopes, key) 33 jwtTok.ClaimSet.Aud = c.Web.TokenURI 34 token, err := jwtTok.Assert(new(http.Client)) 35 if err != nil { 36 return nil, err 37 } 38 39 transport := &oauth.Transport{ 40 Config: &oauth.Config{ 41 ClientId: c.Web.ClientId, 42 Scope: DriverScopes, 43 TokenURL: c.Web.TokenURI, 44 AuthURL: c.Web.AuthURI, 45 }, 46 Token: token, 47 } 48 49 log.Printf("[INFO] Instantiating client...") 50 service, err := compute.New(transport.Client()) 51 if err != nil { 52 return nil, err 53 } 54 55 return &driverGCE{ 56 projectId: projectId, 57 service: service, 58 ui: ui, 59 }, nil 60 } 61 62 func (d *driverGCE) CreateImage(name, description, url string) <-chan error { 63 image := &compute.Image{ 64 Description: description, 65 Name: name, 66 RawDisk: &compute.ImageRawDisk{ 67 ContainerType: "TAR", 68 Source: url, 69 }, 70 SourceType: "RAW", 71 } 72 73 errCh := make(chan error, 1) 74 op, err := d.service.Images.Insert(d.projectId, image).Do() 75 if err != nil { 76 errCh <- err 77 } else { 78 go waitForState(errCh, "DONE", d.refreshGlobalOp(op)) 79 } 80 81 return errCh 82 } 83 84 func (d *driverGCE) DeleteImage(name string) <-chan error { 85 errCh := make(chan error, 1) 86 op, err := d.service.Images.Delete(d.projectId, name).Do() 87 if err != nil { 88 errCh <- err 89 } else { 90 go waitForState(errCh, "DONE", d.refreshGlobalOp(op)) 91 } 92 93 return errCh 94 } 95 96 func (d *driverGCE) DeleteInstance(zone, name string) (<-chan error, error) { 97 op, err := d.service.Instances.Delete(d.projectId, zone, name).Do() 98 if err != nil { 99 return nil, err 100 } 101 102 errCh := make(chan error, 1) 103 go waitForState(errCh, "DONE", d.refreshZoneOp(zone, op)) 104 return errCh, nil 105 } 106 107 func (d *driverGCE) GetNatIP(zone, name string) (string, error) { 108 instance, err := d.service.Instances.Get(d.projectId, zone, name).Do() 109 if err != nil { 110 return "", err 111 } 112 113 for _, ni := range instance.NetworkInterfaces { 114 if ni.AccessConfigs == nil { 115 continue 116 } 117 118 for _, ac := range ni.AccessConfigs { 119 if ac.NatIP != "" { 120 return ac.NatIP, nil 121 } 122 } 123 } 124 125 return "", nil 126 } 127 128 func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) { 129 // Get the zone 130 d.ui.Message(fmt.Sprintf("Loading zone: %s", c.Zone)) 131 zone, err := d.service.Zones.Get(d.projectId, c.Zone).Do() 132 if err != nil { 133 return nil, err 134 } 135 136 // Get the image 137 d.ui.Message(fmt.Sprintf("Loading image: %s", c.Image)) 138 image, err := d.getImage(c.Image) 139 if err != nil { 140 return nil, err 141 } 142 143 // Get the machine type 144 d.ui.Message(fmt.Sprintf("Loading machine type: %s", c.MachineType)) 145 machineType, err := d.service.MachineTypes.Get( 146 d.projectId, zone.Name, c.MachineType).Do() 147 if err != nil { 148 return nil, err 149 } 150 // TODO(mitchellh): deprecation warnings 151 152 // Get the network 153 d.ui.Message(fmt.Sprintf("Loading network: %s", c.Network)) 154 network, err := d.service.Networks.Get(d.projectId, c.Network).Do() 155 if err != nil { 156 return nil, err 157 } 158 159 // Build up the metadata 160 metadata := make([]*compute.MetadataItems, len(c.Metadata)) 161 for k, v := range c.Metadata { 162 metadata = append(metadata, &compute.MetadataItems{ 163 Key: k, 164 Value: v, 165 }) 166 } 167 168 // Create the instance information 169 instance := compute.Instance{ 170 Description: c.Description, 171 Disks: []*compute.AttachedDisk{ 172 &compute.AttachedDisk{ 173 Type: "PERSISTENT", 174 Mode: "READ_WRITE", 175 Kind: "compute#attachedDisk", 176 Boot: true, 177 AutoDelete: true, 178 InitializeParams: &compute.AttachedDiskInitializeParams{ 179 SourceImage: image.SelfLink, 180 }, 181 }, 182 }, 183 MachineType: machineType.SelfLink, 184 Metadata: &compute.Metadata{ 185 Items: metadata, 186 }, 187 Name: c.Name, 188 NetworkInterfaces: []*compute.NetworkInterface{ 189 &compute.NetworkInterface{ 190 AccessConfigs: []*compute.AccessConfig{ 191 &compute.AccessConfig{ 192 Name: "AccessConfig created by Packer", 193 Type: "ONE_TO_ONE_NAT", 194 }, 195 }, 196 Network: network.SelfLink, 197 }, 198 }, 199 ServiceAccounts: []*compute.ServiceAccount{ 200 &compute.ServiceAccount{ 201 Email: "default", 202 Scopes: []string{ 203 "https://www.googleapis.com/auth/userinfo.email", 204 "https://www.googleapis.com/auth/compute", 205 "https://www.googleapis.com/auth/devstorage.full_control", 206 }, 207 }, 208 }, 209 Tags: &compute.Tags{ 210 Items: c.Tags, 211 }, 212 } 213 214 d.ui.Message("Requesting instance creation...") 215 op, err := d.service.Instances.Insert(d.projectId, zone.Name, &instance).Do() 216 if err != nil { 217 return nil, err 218 } 219 220 errCh := make(chan error, 1) 221 go waitForState(errCh, "DONE", d.refreshZoneOp(zone.Name, op)) 222 return errCh, nil 223 } 224 225 func (d *driverGCE) WaitForInstance(state, zone, name string) <-chan error { 226 errCh := make(chan error, 1) 227 go waitForState(errCh, state, d.refreshInstanceState(zone, name)) 228 return errCh 229 } 230 231 func (d *driverGCE) getImage(name string) (image *compute.Image, err error) { 232 projects := []string{d.projectId, "debian-cloud", "centos-cloud"} 233 for _, project := range projects { 234 image, err = d.service.Images.Get(project, name).Do() 235 if err == nil && image != nil && image.SelfLink != "" { 236 return 237 } 238 image = nil 239 } 240 241 if err == nil { 242 err = fmt.Errorf("Image could not be found: %s", name) 243 } 244 245 return 246 } 247 248 func (d *driverGCE) refreshInstanceState(zone, name string) stateRefreshFunc { 249 return func() (string, error) { 250 instance, err := d.service.Instances.Get(d.projectId, zone, name).Do() 251 if err != nil { 252 return "", err 253 } 254 return instance.Status, nil 255 } 256 } 257 258 func (d *driverGCE) refreshGlobalOp(op *compute.Operation) stateRefreshFunc { 259 return func() (string, error) { 260 newOp, err := d.service.GlobalOperations.Get(d.projectId, op.Name).Do() 261 if err != nil { 262 return "", err 263 } 264 265 // If the op is done, check for errors 266 err = nil 267 if newOp.Status == "DONE" { 268 if newOp.Error != nil { 269 for _, e := range newOp.Error.Errors { 270 err = packer.MultiErrorAppend(err, fmt.Errorf(e.Message)) 271 } 272 } 273 } 274 275 return newOp.Status, err 276 } 277 } 278 279 func (d *driverGCE) refreshZoneOp(zone string, op *compute.Operation) stateRefreshFunc { 280 return func() (string, error) { 281 newOp, err := d.service.ZoneOperations.Get(d.projectId, zone, op.Name).Do() 282 if err != nil { 283 return "", err 284 } 285 286 // If the op is done, check for errors 287 err = nil 288 if newOp.Status == "DONE" { 289 if newOp.Error != nil { 290 for _, e := range newOp.Error.Errors { 291 err = packer.MultiErrorAppend(err, fmt.Errorf(e.Message)) 292 } 293 } 294 } 295 296 return newOp.Status, err 297 } 298 } 299 300 // stateRefreshFunc is used to refresh the state of a thing and is 301 // used in conjunction with waitForState. 302 type stateRefreshFunc func() (string, error) 303 304 // waitForState will spin in a loop forever waiting for state to 305 // reach a certain target. 306 func waitForState(errCh chan<- error, target string, refresh stateRefreshFunc) { 307 for { 308 state, err := refresh() 309 if err != nil { 310 errCh <- err 311 return 312 } 313 if state == target { 314 errCh <- nil 315 return 316 } 317 318 time.Sleep(2 * time.Second) 319 } 320 }