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  }