github.com/sneal/packer@v0.5.2/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/v1beta16" 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 Image: image.SelfLink, 172 MachineType: machineType.SelfLink, 173 Metadata: &compute.Metadata{ 174 Items: metadata, 175 }, 176 Name: c.Name, 177 NetworkInterfaces: []*compute.NetworkInterface{ 178 &compute.NetworkInterface{ 179 AccessConfigs: []*compute.AccessConfig{ 180 &compute.AccessConfig{ 181 Name: "AccessConfig created by Packer", 182 Type: "ONE_TO_ONE_NAT", 183 }, 184 }, 185 Network: network.SelfLink, 186 }, 187 }, 188 ServiceAccounts: []*compute.ServiceAccount{ 189 &compute.ServiceAccount{ 190 Email: "default", 191 Scopes: []string{ 192 "https://www.googleapis.com/auth/userinfo.email", 193 "https://www.googleapis.com/auth/compute", 194 "https://www.googleapis.com/auth/devstorage.full_control", 195 }, 196 }, 197 }, 198 Tags: &compute.Tags{ 199 Items: c.Tags, 200 }, 201 } 202 203 d.ui.Message("Requesting instance creation...") 204 op, err := d.service.Instances.Insert(d.projectId, zone.Name, &instance).Do() 205 if err != nil { 206 return nil, err 207 } 208 209 errCh := make(chan error, 1) 210 go waitForState(errCh, "DONE", d.refreshZoneOp(zone.Name, op)) 211 return errCh, nil 212 } 213 214 func (d *driverGCE) WaitForInstance(state, zone, name string) <-chan error { 215 errCh := make(chan error, 1) 216 go waitForState(errCh, state, d.refreshInstanceState(zone, name)) 217 return errCh 218 } 219 220 func (d *driverGCE) getImage(name string) (image *compute.Image, err error) { 221 projects := []string{d.projectId, "debian-cloud", "centos-cloud"} 222 for _, project := range projects { 223 image, err = d.service.Images.Get(project, name).Do() 224 if err == nil && image != nil && image.SelfLink != "" { 225 return 226 } 227 image = nil 228 } 229 230 if err == nil { 231 err = fmt.Errorf("Image could not be found: %s", name) 232 } 233 234 return 235 } 236 237 func (d *driverGCE) refreshInstanceState(zone, name string) stateRefreshFunc { 238 return func() (string, error) { 239 instance, err := d.service.Instances.Get(d.projectId, zone, name).Do() 240 if err != nil { 241 return "", err 242 } 243 return instance.Status, nil 244 } 245 } 246 247 func (d *driverGCE) refreshGlobalOp(op *compute.Operation) stateRefreshFunc { 248 return func() (string, error) { 249 newOp, err := d.service.GlobalOperations.Get(d.projectId, op.Name).Do() 250 if err != nil { 251 return "", err 252 } 253 254 // If the op is done, check for errors 255 err = nil 256 if newOp.Status == "DONE" { 257 if newOp.Error != nil { 258 for _, e := range newOp.Error.Errors { 259 err = packer.MultiErrorAppend(err, fmt.Errorf(e.Message)) 260 } 261 } 262 } 263 264 return newOp.Status, err 265 } 266 } 267 268 func (d *driverGCE) refreshZoneOp(zone string, op *compute.Operation) stateRefreshFunc { 269 return func() (string, error) { 270 newOp, err := d.service.ZoneOperations.Get(d.projectId, zone, op.Name).Do() 271 if err != nil { 272 return "", err 273 } 274 275 // If the op is done, check for errors 276 err = nil 277 if newOp.Status == "DONE" { 278 if newOp.Error != nil { 279 for _, e := range newOp.Error.Errors { 280 err = packer.MultiErrorAppend(err, fmt.Errorf(e.Message)) 281 } 282 } 283 } 284 285 return newOp.Status, err 286 } 287 } 288 289 // stateRefreshFunc is used to refresh the state of a thing and is 290 // used in conjunction with waitForState. 291 type stateRefreshFunc func() (string, error) 292 293 // waitForState will spin in a loop forever waiting for state to 294 // reach a certain target. 295 func waitForState(errCh chan<- error, target string, refresh stateRefreshFunc) { 296 for { 297 state, err := refresh() 298 if err != nil { 299 errCh <- err 300 return 301 } 302 if state == target { 303 errCh <- nil 304 return 305 } 306 307 time.Sleep(2 * time.Second) 308 } 309 }