private MachineType getClosestInstanceType(MachineCreationRequest request) throws OpsException {
    log.info("Listing machine types to find best size");

    Iterable<MachineType> flavors = listMachineTypes(projectId);
    List<MachineType> candidates = Lists.newArrayList();
    for (MachineType flavor : flavors) {
      // Ignore the machine types with no ephemeral disks - they're the same price
      // TODO: Is this right?
      if (flavor.getEphemeralDisks() == null || flavor.getEphemeralDisks().isEmpty()) {
        continue;
      }
      int instanceMemoryMB = flavor.getMemoryMb();
      if (request.minimumMemoryMB > instanceMemoryMB) {
        continue;
      }

      candidates.add(flavor);
    }

    MachineType bestFlavor = findCheapest(candidates);
    if (bestFlavor == null) {
      return null;
    }

    return bestFlavor;
  }
  private float computePrice(MachineType flavor) {
    // We compute the per-hour price so we can choose the smallest/cheapest instance size

    float price = 0;

    int cpus = flavor.getGuestCpus() != null ? flavor.getGuestCpus() : 0;
    int memoryMb = flavor.getMemoryMb() != null ? flavor.getMemoryMb() : 0;

    // // RAM is $0.10 / hour / GB
    // price += (0.10 / 1024.0) * ram;

    // // Disk is $0.10 / hour / TB
    // int disk = flavor.getDisk();
    // price += (0.10 / 1024.0) * disk;

    // CPUs are $0.145 / hour / vCPU
    price += 0.145 * cpus;

    return price;
  }
  public Instance createInstance(
      GoogleCloud cloud, MachineCreationRequest request, PublicKey sshPublicKey)
      throws OpsException {
    // GoogleComputeClient computeClient = getComputeClient(cloud);

    try {
      Image foundImage = null;

      {
        DiskImageRecipe recipe = null;
        if (request.recipeId != null) {
          recipe = platformLayerClient.getItem(request.recipeId, DiskImageRecipe.class);
        }

        OperatingSystemRecipe operatingSystem = null;
        if (recipe != null) {
          operatingSystem = recipe.getOperatingSystem();
        }

        log.info("Listing images to pick best image");
        Iterable<Image> images = listImages(PROJECTID_GOOGLE);

        // TODO: We need a better solution here!!
        log.warn("Hard coding image names");
        Set<String> imageNames = Sets.newHashSet("ubuntu-12-04-v20120621");
        // Set<String> imageNames = Sets.newHashSet("centos-6-2-v20120621");

        for (Image image : images) {
          if (imageNames.contains(image.getName())) {
            foundImage = image;
            break;
          }
        }

        if (foundImage == null) {
          throw new IllegalArgumentException("Could not find image");
        }
      }

      // GCE requires that the name comply with RFC1035, which I think means a valid DNS
      // For now, just use a UUID, with a pl- prefix so it doesn't start with a number
      // TODO: Fix this!
      String instanceName = "pl-" + UUID.randomUUID().toString();

      Operation createServerOperation;
      {
        Instance create = new Instance();

        create.setName(instanceName);

        create.setZone(buildZoneUrl(projectId, ZONE_US_CENTRAL1_A));

        {
          NetworkInterface networkInterface = new NetworkInterface();
          networkInterface.setNetwork(buildNetworkUrl(projectId, "default"));

          AccessConfig networkAccessConfig = new AccessConfig();
          networkAccessConfig.setType("ONE_TO_ONE_NAT");

          networkInterface.setAccessConfigs(Lists.newArrayList(networkAccessConfig));

          create.setNetworkInterfaces(Lists.newArrayList(networkInterface));
        }

        Metadata metadata = new Metadata();
        metadata.setItems(Lists.<Items>newArrayList());
        create.setMetadata(metadata);

        if (request.tags != null) {
          for (Tag tag : request.tags) {
            Metadata.Items meta = new Metadata.Items();
            meta.setKey(tag.getKey());
            meta.setValue(tag.getValue());
            metadata.getItems().add(meta);
          }
        }

        if (request.sshPublicKey != null) {
          Metadata.Items meta = new Metadata.Items();
          meta.setKey("sshKeys");
          meta.setValue(USER_NAME + ":" + OpenSshUtils.serialize(sshPublicKey));

          metadata.getItems().add(meta);
        }

        create.setImage(foundImage.getSelfLink());

        MachineType flavor = getClosestInstanceType(request);
        if (flavor == null) {
          throw new OpsException("Cannot determine machine type for request");
        }
        create.setMachineType(flavor.getSelfLink());

        if (request.securityGroups != null) {
          // TODO: Reimplement if needed
          throw new UnsupportedOperationException();
        }

        // if (createdSecurityGroup != null) {
        // ServerForCreate.SecurityGroup serverSecurityGroup = new ServerForCreate.SecurityGroup();
        // serverSecurityGroup.setName(createdSecurityGroup.getName());
        // create.getSecurityGroups().add(serverSecurityGroup);
        // }

        // create.setConfigDrive(cloudBehaviours.useConfigDrive());

        log.info("Launching new server: " + instanceName);
        try {
          createServerOperation = compute.instances().insert(projectId, create).execute();
        } catch (IOException e) {
          throw new OpsException("Error launching new instance", e);
        }
      }

      log.info("Waiting for server to be ready");
      createServerOperation = waitComplete(createServerOperation, 10, TimeUnit.MINUTES);

      Instance created;

      InstanceState state = null;
      while (true) {
        created = findInstanceByName(instanceName);

        state = InstanceState.get(created);
        log.info("Instance state: " + state);

        if (state.isRunning()) {
          break;
        }

        Thread.sleep(1000);
      }

      return created;
    } catch (InterruptedException e) {
      ExceptionUtils.handleInterrupted(e);
      throw new OpsException("Error building server", e);
    } catch (TimeoutException e) {
      throw new OpsException("Timeout waiting for server build", e);
    }
  }