You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ad...@apache.org on 2014/11/06 20:52:28 UTC

[2/2] jclouds-labs-google git commit: Add GoogleComputeEngineProviderMetadata and implement dynamic Location suppliers.

Add GoogleComputeEngineProviderMetadata and implement dynamic Location suppliers.


Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/commit/867c785b
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/tree/867c785b
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/diff/867c785b

Branch: refs/heads/master
Commit: 867c785bea1ca78eb11c66c8937e51ecab8f813d
Parents: d740323
Author: Adrian Cole <ac...@twitter.com>
Authored: Wed Nov 5 23:02:53 2014 -0800
Committer: Adrian Cole <ac...@twitter.com>
Committed: Thu Nov 6 11:43:04 2014 -0800

----------------------------------------------------------------------
 .../GoogleComputeEngineApiMetadata.java         |  38 ++-
 .../GoogleComputeEngineConstants.java           |  20 --
 .../GoogleComputeEngineProviderMetadata.java    |  72 ++++++
 .../GoogleComputeEngineServiceAdapter.java      |  37 ++-
 ...GoogleComputeEngineServiceContextModule.java | 256 +++++++------------
 .../functions/BuildInstanceMetadata.java        |   9 +-
 .../functions/CreateNetworkIfNeeded.java        |   6 -
 .../functions/InstanceInZoneToNodeMetadata.java |  26 +-
 .../functions/MachineTypeInZoneToHardware.java  |  41 ++-
 .../functions/OrphanedGroupsFromDeadNodes.java  |  12 +-
 .../compute/functions/RegionToLocation.java     |  45 ----
 .../compute/functions/ZoneToLocation.java       |  45 ----
 .../GoogleComputeEngineTemplateOptions.java     |   2 +-
 .../GoogleComputeEngineHttpApiModule.java       |  76 ++----
 .../GoogleComputeEngineLocationModule.java      | 194 ++++++++++++++
 .../config/GoogleComputeEngineParserModule.java |  40 +--
 .../RegionOperationDonePredicate.java           |   9 +-
 .../predicates/ZoneOperationDonePredicate.java  |   9 +-
 .../GoogleComputeEngineApiMetadataTest.java     |  42 ---
 ...GoogleComputeEngineProviderMetadataTest.java |  34 +++
 .../GoogleComputeEngineServiceExpectTest.java   | 244 +++++++++---------
 .../InstanceInZoneToNodeMetadataTest.java       |  12 +-
 .../OrphanedGroupsFromDeadNodesTest.java        |  18 +-
 .../features/InstanceApiLiveTest.java           |   1 -
 .../features/RegionApiExpectTest.java           |   5 +-
 .../BaseGoogleComputeEngineExpectTest.java      |  66 ++---
 .../parse/ParseRegionListTest.java              |   2 +-
 .../parse/ParseRegionTest.java                  |   4 +-
 .../src/test/resources/region_get.json          |   4 +-
 .../src/test/resources/region_list.json         |   6 +-
 30 files changed, 682 insertions(+), 693 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java
index 6584d18..0c9a23c 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java
@@ -19,7 +19,6 @@ package org.jclouds.googlecomputeengine;
 import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
 import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE;
 import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.GCE_IMAGE_PROJECTS;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.GCE_PROVIDER_NAME;
 import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.OPERATION_COMPLETE_INTERVAL;
 import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.OPERATION_COMPLETE_TIMEOUT;
 import static org.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE;
@@ -29,7 +28,6 @@ import static org.jclouds.reflect.Reflection2.typeToken;
 import java.net.URI;
 import java.util.Properties;
 
-import org.jclouds.apis.ApiMetadata;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.googlecomputeengine.compute.config.GoogleComputeEngineServiceContextModule;
 import org.jclouds.googlecomputeengine.config.GoogleComputeEngineHttpApiModule;
@@ -38,11 +36,9 @@ import org.jclouds.oauth.v2.config.OAuthAuthenticationModule;
 import org.jclouds.oauth.v2.config.OAuthModule;
 import org.jclouds.rest.internal.BaseHttpApiMetadata;
 
-import com.google.auto.service.AutoService;
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Module;
 
-@AutoService(ApiMetadata.class)
 public class GoogleComputeEngineApiMetadata extends BaseHttpApiMetadata<GoogleComputeEngineApi> {
 
    @Override
@@ -64,9 +60,9 @@ public class GoogleComputeEngineApiMetadata extends BaseHttpApiMetadata<GoogleCo
       properties.put(AUDIENCE, "https://accounts.google.com/o/oauth2/token");
       properties.put(SIGNATURE_OR_MAC_ALGORITHM, "RS256");
       properties.put(PROPERTY_SESSION_INTERVAL, 3600);
-      properties.setProperty(TEMPLATE, "osFamily=DEBIAN,osVersionMatches=7\\..*,locationId=us-central1-a,loginUser=jclouds");
       properties.put(OPERATION_COMPLETE_INTERVAL, 500);
       properties.put(OPERATION_COMPLETE_TIMEOUT, 600000);
+      properties.put(TEMPLATE, "osFamily=DEBIAN,osVersionMatches=7\\..*,locationId=us-central1-a,loginUser=jclouds");
       properties.put(GCE_IMAGE_PROJECTS, "centos-cloud,debian-cloud,rhel-cloud,suse-cloud,opensuse-cloud,gce-nvme,coreos-cloud");
       return properties;
    }
@@ -74,22 +70,22 @@ public class GoogleComputeEngineApiMetadata extends BaseHttpApiMetadata<GoogleCo
    public static class Builder extends BaseHttpApiMetadata.Builder<GoogleComputeEngineApi, Builder> {
 
       protected Builder() {
-         id(GCE_PROVIDER_NAME)
-                 .name("Google Compute Engine Api")
-                 .identityName("Email associated with the Google API client_id")
-                 .credentialName("Private key literal associated with the Google API client_id")
-                 .documentation(URI.create("https://developers.google.com/compute/docs"))
-                 .version("v1")
-                 .defaultEndpoint("https://www.googleapis.com/compute/v1")
-                 .defaultProperties(GoogleComputeEngineApiMetadata.defaultProperties())
-                 .view(typeToken(ComputeServiceContext.class))
-                 .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
-                         .add(GoogleComputeEngineHttpApiModule.class)
-                         .add(GoogleComputeEngineParserModule.class)
-                         .add(OAuthAuthenticationModule.class)
-                         .add(OAuthModule.class)
-                         .add(GoogleComputeEngineServiceContextModule.class)
-                         .build());
+         id("google-compute-engine")
+           .name("Google Compute Engine Api")
+           .identityName("Email associated with the Google API client_id")
+           .credentialName("Private key literal associated with the Google API client_id")
+           .documentation(URI.create("https://developers.google.com/compute/docs"))
+           .version("v1")
+           .defaultEndpoint("https://www.googleapis.com/compute/v1")
+           .defaultProperties(GoogleComputeEngineApiMetadata.defaultProperties())
+           .view(typeToken(ComputeServiceContext.class))
+           .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
+                   .add(GoogleComputeEngineHttpApiModule.class)
+                   .add(GoogleComputeEngineParserModule.class)
+                   .add(OAuthAuthenticationModule.class)
+                   .add(OAuthModule.class)
+                   .add(GoogleComputeEngineServiceContextModule.class)
+                   .build());
       }
 
       @Override

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineConstants.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineConstants.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineConstants.java
index 16c68a8..1ab700f 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineConstants.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineConstants.java
@@ -16,30 +16,14 @@
  */
 package org.jclouds.googlecomputeengine;
 
-import org.jclouds.domain.Location;
-import org.jclouds.domain.LocationBuilder;
-import org.jclouds.domain.LocationScope;
-
 import com.google.common.annotations.Beta;
 
 public final class GoogleComputeEngineConstants {
 
-   public static final String GCE_PROVIDER_NAME = "google-compute-engine";
-
-   /**
-    * The name of the project that keeps public resources.
-    */
-   public static final String GOOGLE_PROJECT = "google";
-
    public static final String COMPUTE_SCOPE = "https://www.googleapis.com/auth/compute";
 
    public static final String COMPUTE_READONLY_SCOPE = "https://www.googleapis.com/auth/compute.readonly";
 
-   public static final String STORAGE_READONLY_SCOPE = "https://www.googleapis.com/auth/devstorage.read_only";
-
-   public static final String STORAGE_WRITEONLY_SCOPE = "https://www.googleapis.com/auth/devstorage.write_only";
-
-
    /**
     * The total time, in msecs, to wait for an operation to complete.
     */
@@ -52,16 +36,12 @@ public final class GoogleComputeEngineConstants {
    @Beta
    public static final String OPERATION_COMPLETE_INTERVAL = "jclouds.google-compute-engine.operation-complete-interval";
 
-   public static final Location GOOGLE_PROVIDER_LOCATION = new LocationBuilder().scope(LocationScope.PROVIDER).id
-           (GCE_PROVIDER_NAME).description(GCE_PROVIDER_NAME).build();
-
    /**
     * The list of projects that will be scanned looking for images.
     */
    @Beta
    public static final String GCE_IMAGE_PROJECTS = "jclouds.google-compute-engine.image-projects";
 
-
    /**
     * The key we look for in instance metadata for the URI for the image the instance was created from.
     */

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineProviderMetadata.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineProviderMetadata.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineProviderMetadata.java
new file mode 100644
index 0000000..43c8855
--- /dev/null
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineProviderMetadata.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.googlecomputeengine;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadata;
+
+import com.google.auto.service.AutoService;
+
+/** Note: This does not set iso3166Codes as Google intentionally does not document them. */
+@AutoService(ProviderMetadata.class)
+public final class GoogleComputeEngineProviderMetadata extends BaseProviderMetadata {
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   @Override
+   public Builder toBuilder() {
+      return builder().fromProviderMetadata(this);
+   }
+
+   public GoogleComputeEngineProviderMetadata() {
+      super(builder());
+   }
+
+   public GoogleComputeEngineProviderMetadata(Builder builder) {
+      super(builder);
+   }
+
+   public static Properties defaultProperties() {
+      return new Properties(); // currently all are set in the api metadata class.
+   }
+
+   public static final class Builder extends BaseProviderMetadata.Builder {
+
+      private Builder() {
+         id("google-compute-engine") //
+         .name("Google Compute Engine") //
+         .apiMetadata(new GoogleComputeEngineApiMetadata()) //
+         .homepage(URI.create("https://cloud.google.com/compute")) //
+         .console(URI.create("https://console.developers.google.com/project")) //
+         .defaultProperties(GoogleComputeEngineProviderMetadata.defaultProperties());
+      }
+
+      @Override public GoogleComputeEngineProviderMetadata build() {
+         return new GoogleComputeEngineProviderMetadata(this);
+      }
+
+      @Override public Builder fromProviderMetadata(ProviderMetadata in) {
+         super.fromProviderMetadata(in);
+         return this;
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java
index 0a2bc7d..4299eab 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java
@@ -38,7 +38,6 @@ import static org.jclouds.util.Predicates2.retry;
 import java.net.URI;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -47,7 +46,6 @@ import java.util.concurrent.atomic.AtomicReference;
 import javax.inject.Inject;
 import javax.inject.Named;
 
-import org.jclouds.collect.Memoized;
 import org.jclouds.compute.ComputeServiceAdapter;
 import org.jclouds.compute.domain.Hardware;
 import org.jclouds.compute.domain.Template;
@@ -69,15 +67,16 @@ import org.jclouds.googlecomputeengine.domain.Instance.AttachedDisk.Mode;
 import org.jclouds.googlecomputeengine.domain.ListPage;
 import org.jclouds.googlecomputeengine.domain.MachineType;
 import org.jclouds.googlecomputeengine.domain.Operation;
-import org.jclouds.googlecomputeengine.domain.Zone;
 import org.jclouds.googlecomputeengine.domain.templates.InstanceTemplate;
 import org.jclouds.googlecomputeengine.domain.templates.InstanceTemplate.PersistentDisk;
 import org.jclouds.googlecomputeengine.features.DiskApi;
 import org.jclouds.googlecomputeengine.features.InstanceApi;
 import org.jclouds.googlecomputeengine.options.DiskCreationOptions;
+import org.jclouds.location.Zone;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
 import com.google.common.base.Supplier;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
@@ -88,11 +87,12 @@ import com.google.common.primitives.Ints;
 import com.google.common.util.concurrent.Atomics;
 import com.google.common.util.concurrent.UncheckedTimeoutException;
 
-public final class GoogleComputeEngineServiceAdapter implements ComputeServiceAdapter<InstanceInZone, MachineTypeInZone, Image, Zone> {
+public final class GoogleComputeEngineServiceAdapter
+      implements ComputeServiceAdapter<InstanceInZone, MachineTypeInZone, Image, Location> {
 
    private final GoogleComputeEngineApi api;
    private final Supplier<String> userProject;
-   private final Supplier<Map<URI, ? extends Location>> zones;
+   private final Supplier<Set<String>> zoneIds;
    private final Function<TemplateOptions, ImmutableMap.Builder<String, String>> metatadaFromTemplateOptions;
    private final Predicate<AtomicReference<Operation>> retryOperationDonePredicate;
    private final long operationCompleteCheckInterval;
@@ -107,9 +107,9 @@ public final class GoogleComputeEngineServiceAdapter implements ComputeServiceAd
                                             @Named("zone") Predicate<AtomicReference<Operation>> operationDonePredicate,
                                             @Named(OPERATION_COMPLETE_INTERVAL) Long operationCompleteCheckInterval,
                                             @Named(OPERATION_COMPLETE_TIMEOUT) Long operationCompleteCheckTimeout,
-                                            @Memoized Supplier<Map<URI, ? extends Location>> zones,
+                                            @Zone Supplier<Set<String>> zoneIds,
                                             FirewallTagNamingConvention.Factory firewallTagNamingConvention,
-                                            @Named(GCE_IMAGE_PROJECTS) List<String> imageProjects) {
+                                            @Named(GCE_IMAGE_PROJECTS) String imageProjects) {
       this.api = api;
       this.userProject = userProject;
       this.metatadaFromTemplateOptions = metatadaFromTemplateOptions;
@@ -117,9 +117,9 @@ public final class GoogleComputeEngineServiceAdapter implements ComputeServiceAd
       this.operationCompleteCheckTimeout = operationCompleteCheckTimeout;
       this.retryOperationDonePredicate = retry(operationDonePredicate, operationCompleteCheckTimeout,
                                                operationCompleteCheckInterval, TimeUnit.MILLISECONDS);
-      this.zones = zones;
+      this.zoneIds = zoneIds;
       this.firewallTagNamingConvention = firewallTagNamingConvention;
-      this.imageProjects = imageProjects;
+      this.imageProjects = Splitter.on(',').splitToList(imageProjects);
    }
 
    @Override
@@ -241,8 +241,8 @@ public final class GoogleComputeEngineServiceAdapter implements ComputeServiceAd
    public Iterable<MachineTypeInZone> listHardwareProfiles() {
       ImmutableList.Builder<MachineTypeInZone> builder = ImmutableList.builder();
 
-      for (final Location zone : zones.get().values()) {
-         for (Iterator<ListPage<MachineType>> i = api.getMachineTypeApi(userProject.get(), zone.getId()).list();
+      for (final String zoneId : zoneIds.get()) {
+         for (Iterator<ListPage<MachineType>> i = api.getMachineTypeApi(userProject.get(), zoneId).list();
                i.hasNext(); ) {
             builder.addAll(FluentIterable.from(i.next()).filter(new Predicate<MachineType>() {
                @Override public boolean apply(MachineType input) {
@@ -254,7 +254,6 @@ public final class GoogleComputeEngineServiceAdapter implements ComputeServiceAd
                }
             }));
          }
-
       }
 
       return builder.build();
@@ -288,8 +287,8 @@ public final class GoogleComputeEngineServiceAdapter implements ComputeServiceAd
    }
 
    @Override
-   public Iterable<Zone> listLocations() {
-      return concat(api.getZoneApi(userProject.get()).list());
+   public Iterable<Location> listLocations() {
+      throw new UnsupportedOperationException("Locations are configured in GoogleComputeEngineLocationModule");
    }
 
    @Override
@@ -301,13 +300,13 @@ public final class GoogleComputeEngineServiceAdapter implements ComputeServiceAd
 
    @Override
    public Iterable<InstanceInZone> listNodes() {
-      return FluentIterable.from(zones.get().values())
-            .transformAndConcat(new Function<Location, Iterable<InstanceInZone>>() {
-               @Override public Iterable<InstanceInZone> apply(final Location input) {
-                  return transform(concat(api.getInstanceApi(userProject.get(), input.getId()).list()),
+      return FluentIterable.from(zoneIds.get())
+            .transformAndConcat(new Function<String, Iterable<InstanceInZone>>() {
+               @Override public Iterable<InstanceInZone> apply(final String zoneId) {
+                  return transform(concat(api.getInstanceApi(userProject.get(), zoneId).list()),
                         new Function<Instance, InstanceInZone>() {
                            @Override public InstanceInZone apply(Instance arg0) {
-                              return InstanceInZone.create(arg0, input.getId());
+                              return InstanceInZone.create(arg0, zoneId);
                            }
                         });
                }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
index c75d3e2..76dabd7 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
@@ -16,18 +16,13 @@
  */
 package org.jclouds.googlecomputeengine.compute.config;
 
-import static com.google.common.collect.Iterables.transform;
-import static com.google.common.collect.Maps.uniqueIndex;
+import static com.google.common.base.Suppliers.memoizeWithExpiration;
+import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
-import static org.jclouds.googlecomputeengine.internal.ListPages.concat;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.GCE_IMAGE_PROJECTS;
 
 import java.net.URI;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
 
 import javax.inject.Named;
 import javax.inject.Singleton;
@@ -44,7 +39,6 @@ import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.compute.strategy.PrioritizeCredentialsFromTemplate;
 import org.jclouds.domain.Location;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.compute.GoogleComputeEngineService;
 import org.jclouds.googlecomputeengine.compute.GoogleComputeEngineServiceAdapter;
 import org.jclouds.googlecomputeengine.compute.domain.InstanceInZone;
@@ -61,29 +55,21 @@ import org.jclouds.googlecomputeengine.compute.functions.InstanceInZoneToNodeMet
 import org.jclouds.googlecomputeengine.compute.functions.MachineTypeInZoneToHardware;
 import org.jclouds.googlecomputeengine.compute.functions.NetworkToSecurityGroup;
 import org.jclouds.googlecomputeengine.compute.functions.OrphanedGroupsFromDeadNodes;
-import org.jclouds.googlecomputeengine.compute.functions.RegionToLocation;
-import org.jclouds.googlecomputeengine.compute.functions.ZoneToLocation;
 import org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions;
 import org.jclouds.googlecomputeengine.compute.predicates.AllNodesInGroupTerminated;
 import org.jclouds.googlecomputeengine.compute.strategy.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
 import org.jclouds.googlecomputeengine.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy;
 import org.jclouds.googlecomputeengine.compute.strategy.UseNodeCredentialsButOverrideFromTemplate;
-import org.jclouds.googlecomputeengine.config.UserProject;
 import org.jclouds.googlecomputeengine.domain.Firewall;
 import org.jclouds.googlecomputeengine.domain.Image;
 import org.jclouds.googlecomputeengine.domain.Instance;
 import org.jclouds.googlecomputeengine.domain.Network;
-import org.jclouds.googlecomputeengine.domain.Region;
-import org.jclouds.googlecomputeengine.domain.Zone;
 import org.jclouds.net.domain.IpPermission;
-import org.jclouds.rest.AuthorizationException;
-import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
+import com.google.common.base.Functions;
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
-import com.google.common.base.Splitter;
 import com.google.common.base.Supplier;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
@@ -94,8 +80,8 @@ import com.google.inject.Provides;
 import com.google.inject.Scopes;
 import com.google.inject.TypeLiteral;
 
-public class GoogleComputeEngineServiceContextModule
-        extends ComputeServiceAdapterContextModule<InstanceInZone, MachineTypeInZone, Image, Zone> {
+public final class GoogleComputeEngineServiceContextModule
+      extends ComputeServiceAdapterContextModule<InstanceInZone, MachineTypeInZone, Image, Location> {
 
    @Override
    protected void configure() {
@@ -103,192 +89,126 @@ public class GoogleComputeEngineServiceContextModule
 
       bind(ComputeService.class).to(GoogleComputeEngineService.class);
 
-      bind(new TypeLiteral<ComputeServiceAdapter<InstanceInZone, MachineTypeInZone, Image, Zone>>() {})
-              .to(GoogleComputeEngineServiceAdapter.class);
+      bind(new TypeLiteral<ComputeServiceAdapter<InstanceInZone, MachineTypeInZone, Image, Location>>() {
+      }).to(GoogleComputeEngineServiceAdapter.class);
 
-      bind(new TypeLiteral<Function<InstanceInZone, NodeMetadata>>() {})
-              .to(InstanceInZoneToNodeMetadata.class);
+      bind(new TypeLiteral<Function<Location, Location>>() {
+      }).toInstance(Functions.<Location>identity());
 
-      bind(new TypeLiteral<Function<MachineTypeInZone, Hardware>>() {})
-              .to(MachineTypeInZoneToHardware.class);
+      bind(new TypeLiteral<Function<InstanceInZone, NodeMetadata>>() {
+      }).to(InstanceInZoneToNodeMetadata.class);
 
-      bind(new TypeLiteral<Function<Image, org.jclouds.compute.domain.Image>>() {})
-              .to(GoogleComputeEngineImageToImage.class);
+      bind(new TypeLiteral<Function<MachineTypeInZone, Hardware>>() {
+      }).to(MachineTypeInZoneToHardware.class);
 
-      bind(new TypeLiteral<Function<Region, Location>>() {
-      })
-              .to(RegionToLocation.class);
+      bind(new TypeLiteral<Function<Image, org.jclouds.compute.domain.Image>>() {
+      }).to(GoogleComputeEngineImageToImage.class);
 
-      bind(new TypeLiteral<Function<Zone, Location>>() {})
-              .to(ZoneToLocation.class);
+      bind(new TypeLiteral<Function<Firewall, Iterable<IpPermission>>>() {
+      }).to(FirewallToIpPermission.class);
 
-      bind(new TypeLiteral<Function<Firewall, Iterable<IpPermission>>>() {})
-              .to(FirewallToIpPermission.class);
+      bind(new TypeLiteral<Function<Network, SecurityGroup>>() {
+      }).to(NetworkToSecurityGroup.class);
 
-      bind(new TypeLiteral<Function<Network, SecurityGroup>>() {})
-              .to(NetworkToSecurityGroup.class);
-
-      bind(new TypeLiteral<Function<TemplateOptions, ImmutableMap.Builder<String, String>>>() {})
-              .to(BuildInstanceMetadata.class);
+      bind(new TypeLiteral<Function<TemplateOptions, ImmutableMap.Builder<String, String>>>() {
+      }).to(BuildInstanceMetadata.class);
 
       bind(org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy.class)
-              .to(PopulateDefaultLoginCredentialsForImageStrategy.class);
+            .to(PopulateDefaultLoginCredentialsForImageStrategy.class);
 
-      bind(org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet.class).to(
-              CreateNodesWithGroupEncodedIntoNameThenAddToSet.class);
+      bind(org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet.class)
+            .to(CreateNodesWithGroupEncodedIntoNameThenAddToSet.class);
 
       bind(TemplateOptions.class).to(GoogleComputeEngineTemplateOptions.class);
 
-      bind(new TypeLiteral<Function<Set<? extends NodeMetadata>, Set<String>>>() {})
-              .to(OrphanedGroupsFromDeadNodes.class);
+      bind(new TypeLiteral<Function<Set<? extends NodeMetadata>, Set<String>>>() {
+      }).to(OrphanedGroupsFromDeadNodes.class);
 
-      bind(new TypeLiteral<Predicate<String>>() {}).to(AllNodesInGroupTerminated.class);
+      bind(new TypeLiteral<Predicate<String>>() {
+      }).to(AllNodesInGroupTerminated.class);
 
-      bind(new TypeLiteral<Function<NetworkAndAddressRange, Network>>() {})
-              .to(CreateNetworkIfNeeded.class);
+      bind(new TypeLiteral<Function<NetworkAndAddressRange, Network>>() {
+      }).to(CreateNetworkIfNeeded.class);
 
-      bind(new TypeLiteral<CacheLoader<NetworkAndAddressRange, Network>>() {})
-              .to(FindNetworkOrCreate.class);
+      bind(new TypeLiteral<CacheLoader<NetworkAndAddressRange, Network>>() {
+      }).to(FindNetworkOrCreate.class);
 
-      bind(new TypeLiteral<SecurityGroupExtension>() {})
-              .to(GoogleComputeEngineSecurityGroupExtension.class);
+      bind(SecurityGroupExtension.class).to(GoogleComputeEngineSecurityGroupExtension.class);
 
       bind(PrioritizeCredentialsFromTemplate.class).to(UseNodeCredentialsButOverrideFromTemplate.class);
-
-      install(new LocationsFromComputeServiceAdapterModule<InstanceInZone, MachineTypeInZone, Image, Zone>() {});
-
       bind(FirewallTagNamingConvention.Factory.class).in(Scopes.SINGLETON);
    }
 
-   @Provides
-   @Singleton
-   @Memoized
-   public Supplier<Map<URI, ? extends org.jclouds.compute.domain.Image>> provideImagesMap(
-           AtomicReference<AuthorizationException> authException,
-           final Supplier<Set<? extends org.jclouds.compute.domain.Image>> images,
-           @Named(PROPERTY_SESSION_INTERVAL) long seconds) {
-      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
-              new Supplier<Map<URI, ? extends org.jclouds.compute.domain.Image>>() {
-                 @Override
-                 public Map<URI, ? extends org.jclouds.compute.domain.Image> get() {
-                    return uniqueIndex(images.get(), new Function<org.jclouds.compute.domain.Image, URI>() {
-                       @Override
-                       public URI apply(org.jclouds.compute.domain.Image input) {
-                          return input.getUri();
-                       }
-                    });
-                 }
-              },
-              seconds, TimeUnit.SECONDS);
-   }
-
-   @Provides
-   @Singleton
-   @Memoized
-   public Supplier<Map<URI, ? extends Hardware>> provideHardwaresMap(
-           AtomicReference<AuthorizationException> authException,
-           final Supplier<Set<? extends Hardware>> hardwares,
-           @Named(PROPERTY_SESSION_INTERVAL) long seconds) {
-      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
-              new Supplier<Map<URI, ? extends Hardware>>() {
-                 @Override
-                 public Map<URI, ? extends Hardware> get() {
-                    return uniqueIndex(hardwares.get(), new Function<Hardware, URI>() {
-                       @Override
-                       public URI apply(Hardware input) {
-                          return input.getUri();
-                       }
-                    });
-                 }
-              },
-              seconds, TimeUnit.SECONDS);
+   @Provides @Singleton @Memoized Supplier<Map<URI, org.jclouds.compute.domain.Image>> imageByUri(
+         @Memoized final Supplier<Set<? extends org.jclouds.compute.domain.Image>> imageSupplier,
+         @Named(PROPERTY_SESSION_INTERVAL) long seconds) {
+      return memoizeWithExpiration(new Supplier<Map<URI, org.jclouds.compute.domain.Image>>() {
+         @Override public Map<URI, org.jclouds.compute.domain.Image> get() {
+            ImmutableMap.Builder<URI, org.jclouds.compute.domain.Image> result = ImmutableMap.builder();
+            for (org.jclouds.compute.domain.Image image : imageSupplier.get()) {
+               result.put(image.getUri(), image);
+            }
+            return result.build();
+         }
+      }, seconds, SECONDS);
    }
 
-   @Provides
-   @Singleton
-   @Memoized
-   public Supplier<Map<URI, ? extends Location>> provideZones(
-           AtomicReference<AuthorizationException> authException,
-           final GoogleComputeEngineApi api, final Function<Zone, Location> zoneToLocation,
-           @UserProject final Supplier<String> userProject,
-           @Named(PROPERTY_SESSION_INTERVAL) long seconds) {
-      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
-              new Supplier<Map<URI, ? extends Location>>() {
-                 @Override
-                 public Map<URI, ? extends Location> get() {
-                    return uniqueIndex(transform(concat(api.getZoneApi(userProject.get()).list()), zoneToLocation),
-                            new Function<Location, URI>() {
-                               @Override
-                               public URI apply(Location input) {
-                                  return (URI) input.getMetadata().get("selfLink");
-                               }
-                            });
-                 }
-              },
-              seconds, TimeUnit.SECONDS);
+   @Provides @Singleton @Memoized Supplier<Map<URI, Hardware>> hardwareByUri(
+         @Memoized final Supplier<Set<? extends Hardware>> hardwareSupplier,
+         @Named(PROPERTY_SESSION_INTERVAL) long seconds) {
+      return memoizeWithExpiration(new Supplier<Map<URI, Hardware>>() {
+         @Override public Map<URI, Hardware> get() {
+            ImmutableMap.Builder<URI, Hardware> result = ImmutableMap.builder();
+            for (Hardware hardware : hardwareSupplier.get()) {
+               result.put(hardware.getUri(), hardware);
+            }
+            return result.build();
+         }
+      }, seconds, SECONDS);
    }
 
-   @Provides
-   @Singleton
-   @Memoized
-   public Supplier<Map<URI, Region>> provideRegions(
-           AtomicReference<AuthorizationException> authException,
-           final GoogleComputeEngineApi api,
-           @UserProject final Supplier<String> userProject,
-           @Named(PROPERTY_SESSION_INTERVAL) long seconds) {
-      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
-              new Supplier<Map<URI, Region>>() {
-                 @Override
-                 public Map<URI, Region> get() {
-                    return uniqueIndex(concat(api.getRegionApi(userProject.get()).list()),
-                            new Function<Region, URI>() {
-                               @Override
-                               public URI apply(Region input) {
-                                  return input.selfLink();
-                               }
-                            });
-                 }
-              },
-              seconds, TimeUnit.SECONDS);
+   @Provides @Singleton @Memoized Supplier<Map<URI, Location>> locationsByUri(
+         @Memoized final Supplier<Set<? extends Location>> locations,
+         @Memoized final Supplier<Map<URI, String>> selfLinkToNames, @Named(PROPERTY_SESSION_INTERVAL) long seconds) {
+      return memoizeWithExpiration(new Supplier<Map<URI, Location>>() {
+         @Override public Map<URI, Location> get() {
+            ImmutableMap.Builder<URI, Location> result = ImmutableMap.builder();
+            for (Location location : locations.get()) {
+               for (Map.Entry<URI, String> entry : selfLinkToNames.get().entrySet()) {
+                  if (entry.getValue().equals(location.getId())) {
+                     result.put(entry.getKey(), location);
+                     continue;
+                  }
+               }
+            }
+            return result.build();
+         }
+      }, seconds, SECONDS);
    }
 
-   @Provides
-   @Singleton
-   protected LoadingCache<NetworkAndAddressRange, Network> networkMap(
-           CacheLoader<NetworkAndAddressRange, Network> in) {
+   @Provides @Singleton
+   LoadingCache<NetworkAndAddressRange, Network> networkMap(CacheLoader<NetworkAndAddressRange, Network> in) {
       return CacheBuilder.newBuilder().build(in);
    }
 
-   @Override
-   protected Optional<ImageExtension> provideImageExtension(Injector i) {
+   @Override protected Optional<ImageExtension> provideImageExtension(Injector i) {
       return Optional.absent();
    }
 
-   @Override
-   protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
+   @Override protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
       return Optional.of(i.getInstance(SecurityGroupExtension.class));
    }
 
-   @VisibleForTesting
-   public static final Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus =
-           ImmutableMap.<Instance.Status, NodeMetadata.Status>builder()
-                   .put(Instance.Status.PROVISIONING, NodeMetadata.Status.PENDING)
-                   .put(Instance.Status.STAGING, NodeMetadata.Status.PENDING)
-                   .put(Instance.Status.RUNNING, NodeMetadata.Status.RUNNING)
-                   .put(Instance.Status.STOPPING, NodeMetadata.Status.PENDING)
-                   .put(Instance.Status.STOPPED, NodeMetadata.Status.SUSPENDED)
-                   .put(Instance.Status.TERMINATED, NodeMetadata.Status.TERMINATED).build();
-
-   @Singleton
-   @Provides
-   protected Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus() {
-      return toPortableNodeStatus;
-   }
+   private static final Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus =
+         ImmutableMap.<Instance.Status, NodeMetadata.Status>builder()
+                     .put(Instance.Status.PROVISIONING, NodeMetadata.Status.PENDING)
+                     .put(Instance.Status.STAGING, NodeMetadata.Status.PENDING)
+                     .put(Instance.Status.RUNNING, NodeMetadata.Status.RUNNING)
+                     .put(Instance.Status.STOPPING, NodeMetadata.Status.PENDING)
+                     .put(Instance.Status.STOPPED, NodeMetadata.Status.SUSPENDED)
+                     .put(Instance.Status.TERMINATED, NodeMetadata.Status.TERMINATED).build();
 
-   @Singleton
-   @Provides
-   @Named(GCE_IMAGE_PROJECTS)
-   protected List<String> imageProjects(@Named(GCE_IMAGE_PROJECTS) String imageProjects) {
-      return Splitter.on(',').splitToList(imageProjects);
+   @Provides Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus() {
+      return toPortableNodeStatus;
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/BuildInstanceMetadata.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/BuildInstanceMetadata.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/BuildInstanceMetadata.java
index 80b18b1..5d28b63 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/BuildInstanceMetadata.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/BuildInstanceMetadata.java
@@ -19,8 +19,6 @@ package org.jclouds.googlecomputeengine.compute.functions;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static java.lang.String.format;
 
-import javax.inject.Singleton;
-
 import org.jclouds.compute.options.TemplateOptions;
 
 import com.google.common.base.Function;
@@ -29,11 +27,9 @@ import com.google.common.collect.ImmutableMap;
 /**
  * Prepares metadata from the provided TemplateOptions
  */
-@Singleton
-public class BuildInstanceMetadata implements Function<TemplateOptions, ImmutableMap.Builder<String, String>> {
+public final class BuildInstanceMetadata implements Function<TemplateOptions, ImmutableMap.Builder<String, String>> {
 
-   @Override
-   public ImmutableMap.Builder apply(TemplateOptions input) {
+   @Override public ImmutableMap.Builder apply(TemplateOptions input) {
       ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
       if (input.getPublicKey() != null) {
          builder.put("sshKeys", format("%s:%s %s@localhost", checkNotNull(input.getLoginUser(),
@@ -42,5 +38,4 @@ public class BuildInstanceMetadata implements Function<TemplateOptions, Immutabl
       builder.putAll(input.getUserMetadata());
       return builder;
    }
-
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/CreateNetworkIfNeeded.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/CreateNetworkIfNeeded.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/CreateNetworkIfNeeded.java
index b797b95..4b4b113 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/CreateNetworkIfNeeded.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/CreateNetworkIfNeeded.java
@@ -25,17 +25,14 @@ import static org.jclouds.util.Predicates2.retry;
 
 import java.util.concurrent.atomic.AtomicReference;
 
-import javax.annotation.Resource;
 import javax.inject.Inject;
 import javax.inject.Named;
 
-import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.compute.domain.NetworkAndAddressRange;
 import org.jclouds.googlecomputeengine.config.UserProject;
 import org.jclouds.googlecomputeengine.domain.Network;
 import org.jclouds.googlecomputeengine.domain.Operation;
-import org.jclouds.logging.Logger;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
@@ -43,9 +40,6 @@ import com.google.common.base.Supplier;
 import com.google.common.util.concurrent.Atomics;
 
 public class CreateNetworkIfNeeded implements Function<NetworkAndAddressRange, Network> {
-   @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER)
-   private Logger logger = Logger.NULL;
-
    private final GoogleComputeEngineApi api;
    private final Supplier<String> userProject;
    private final Predicate<AtomicReference<Operation>> operationDonePredicate;

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/InstanceInZoneToNodeMetadata.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/InstanceInZoneToNodeMetadata.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/InstanceInZoneToNodeMetadata.java
index 977c9ab..db43000 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/InstanceInZoneToNodeMetadata.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/InstanceInZoneToNodeMetadata.java
@@ -47,22 +47,22 @@ public final class InstanceInZoneToNodeMetadata implements Function<InstanceInZo
 
    private final Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus;
    private final GroupNamingConvention nodeNamingConvention;
-   private final Supplier<Map<URI, ? extends Image>> images;
-   private final Supplier<Map<URI, ? extends Hardware>> hardwares;
-   private final Supplier<Map<URI, ? extends Location>> locations;
+   private final Supplier<Map<URI, Image>> images;
+   private final Supplier<Map<URI, Hardware>> hardwares;
+   private final Supplier<Map<URI, Location>> locationsByUri;
    private final FirewallTagNamingConvention.Factory firewallTagNamingConvention;
 
    @Inject InstanceInZoneToNodeMetadata(Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus,
                                         GroupNamingConvention.Factory namingConvention,
-                                        @Memoized Supplier<Map<URI, ? extends Image>> images,
-                                        @Memoized Supplier<Map<URI, ? extends Hardware>> hardwares,
-                                        @Memoized Supplier<Map<URI, ? extends Location>> locations,
+                                        @Memoized Supplier<Map<URI, Image>> images,
+                                        @Memoized Supplier<Map<URI, Hardware>> hardwares,
+                                        @Memoized Supplier<Map<URI, Location>> locationsByUri,
                                         FirewallTagNamingConvention.Factory firewallTagNamingConvention) {
       this.toPortableNodeStatus = toPortableNodeStatus;
       this.nodeNamingConvention = namingConvention.createWithoutPrefix();
       this.images = images;
       this.hardwares = hardwares;
-      this.locations = locations;
+      this.locationsByUri = locationsByUri;
       this.firewallTagNamingConvention = checkNotNull(firewallTagNamingConvention, "firewallTagNamingConvention");
    }
 
@@ -77,12 +77,16 @@ public final class InstanceInZoneToNodeMetadata implements Function<InstanceInZo
 
       NodeMetadataBuilder builder = new NodeMetadataBuilder();
 
-      Location location = checkNotNull(locations.get().get(input.zone()), "location for %s", input.zone());
-      builder.id(SlashEncodedIds.from(location.getId(), input.name()).slashEncode())
+      Location zone = locationsByUri.get().get(input.zone());
+      if (zone == null) {
+         throw new IllegalStateException(
+               String.format("zone %s not present in %s", input.zone(), locationsByUri.get().keySet()));
+      }
+      builder.id(SlashEncodedIds.from(zone.getId(), input.name()).slashEncode())
               .name(input.name())
               .providerId(input.id())
               .hostname(input.name())
-              .location(location)
+              .location(zone)
               .hardware(hardwares.get().get(input.machineType()))
               .status(toPortableNodeStatus.get(input.status()))
               .tags(tags)
@@ -96,7 +100,7 @@ public final class InstanceInZoneToNodeMetadata implements Function<InstanceInZo
          try {
             URI imageUri = URI.create(input.metadata().items().get(GCE_IMAGE_METADATA_KEY));
 
-            Map<URI, ? extends Image> imagesMap = images.get();
+            Map<URI, Image> imagesMap = images.get();
 
             Image image = checkNotNull(imagesMap.get(imageUri),
                                        "no image for %s. images: %s", imageUri,

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/MachineTypeInZoneToHardware.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/MachineTypeInZoneToHardware.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/MachineTypeInZoneToHardware.java
index 4ed2a11..fe3be11 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/MachineTypeInZoneToHardware.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/MachineTypeInZoneToHardware.java
@@ -16,13 +16,11 @@
  */
 package org.jclouds.googlecomputeengine.compute.functions;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Iterables.filter;
-import static com.google.common.collect.Iterables.getOnlyElement;
-
 import java.net.URI;
 import java.util.Map;
 
+import javax.inject.Inject;
+
 import org.jclouds.collect.Memoized;
 import org.jclouds.compute.domain.Hardware;
 import org.jclouds.compute.domain.HardwareBuilder;
@@ -36,40 +34,31 @@ import org.jclouds.googlecomputeengine.compute.domain.SlashEncodedIds;
 import org.jclouds.googlecomputeengine.domain.MachineType;
 
 import com.google.common.base.Function;
-import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableSet;
-import com.google.inject.Inject;
 
-/**
- * Transforms a google compute domain specific machine type to a generic Hardware object.
- */
-public class MachineTypeInZoneToHardware implements Function<MachineTypeInZone, Hardware> {
+public final class MachineTypeInZoneToHardware implements Function<MachineTypeInZone, Hardware> {
 
-   private final Supplier<Map<URI, ? extends Location>> locations;
+   private final Supplier<Map<URI, Location>> locationsByUri;
 
-   @Inject
-   public MachineTypeInZoneToHardware(@Memoized Supplier<Map<URI, ? extends Location>> locations) {
-      this.locations = locations;
+   @Inject MachineTypeInZoneToHardware(@Memoized Supplier<Map<URI, Location>> locationsByUri) {
+      this.locationsByUri = locationsByUri;
    }
 
    @Override
-   public Hardware apply(final MachineTypeInZone input) {
-      Iterable<? extends Location> zonesForMachineType = filter(locations.get().values(), new Predicate<Location>() {
-         @Override
-         public boolean apply(Location l) {
-            return l.getId().equals(input.machineType().zone());
-         }
-      });
-
-      Location location = checkNotNull(getOnlyElement(zonesForMachineType),
-              "location for %s",
-              input.machineType().zone());
+   public Hardware apply(MachineTypeInZone input) {
+      URI zoneLink = URI.create(
+            input.machineType().selfLink().toString().replace("/machineTypes/" + input.machineType().name(), ""));
 
+      Location zone = locationsByUri.get().get(zoneLink);
+      if (zone == null) {
+         throw new IllegalStateException(
+               String.format("zone %s not present in %s", zoneLink, locationsByUri.get().keySet()));
+      }
       return new HardwareBuilder()
               .id(SlashEncodedIds.from(input.machineType().zone(), input.machineType().name()).slashEncode())
-              .location(location)
+              .location(zone)
               .name(input.machineType().name())
               .hypervisor("kvm")
               .processor(new Processor(input.machineType().guestCpus(), 1.0))

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/OrphanedGroupsFromDeadNodes.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/OrphanedGroupsFromDeadNodes.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/OrphanedGroupsFromDeadNodes.java
index 3301c8f..09c4a17 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/OrphanedGroupsFromDeadNodes.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/OrphanedGroupsFromDeadNodes.java
@@ -19,7 +19,6 @@ package org.jclouds.googlecomputeengine.compute.functions;
 import java.util.Set;
 
 import javax.inject.Inject;
-import javax.inject.Singleton;
 
 import org.jclouds.compute.domain.NodeMetadata;
 
@@ -27,19 +26,16 @@ import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Sets;
 
-@Singleton
-public class OrphanedGroupsFromDeadNodes implements Function<Set<? extends NodeMetadata>, Set<String>> {
+public final class OrphanedGroupsFromDeadNodes implements Function<Set<? extends NodeMetadata>, Set<String>> {
 
    private final Predicate<String> isOrphanedGroupPredicate;
 
-   @Inject
-   public OrphanedGroupsFromDeadNodes(Predicate<String> isOrphanedGroupPredicate) {
+   @Inject OrphanedGroupsFromDeadNodes(Predicate<String> isOrphanedGroupPredicate) {
       this.isOrphanedGroupPredicate = isOrphanedGroupPredicate;
    }
 
 
-   @Override
-   public Set<String> apply(Set<? extends NodeMetadata> deadNodes) {
+   @Override public Set<String> apply(Set<? extends NodeMetadata> deadNodes) {
       Set<String> groups = Sets.newLinkedHashSet();
       for (NodeMetadata deadNode : deadNodes) {
          groups.add(deadNode.getGroup());
@@ -52,6 +48,4 @@ public class OrphanedGroupsFromDeadNodes implements Function<Set<? extends NodeM
       }
       return orphanedGroups;
    }
-
-
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/RegionToLocation.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/RegionToLocation.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/RegionToLocation.java
deleted file mode 100644
index bd2613f..0000000
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/RegionToLocation.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.googlecomputeengine.compute.functions;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.GOOGLE_PROVIDER_LOCATION;
-
-import org.jclouds.domain.Location;
-import org.jclouds.domain.LocationBuilder;
-import org.jclouds.domain.LocationScope;
-import org.jclouds.googlecomputeengine.domain.Region;
-
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableMap;
-
-/**
- * Transforms a google compute domain specific region to a generic Region object.
- */
-public class RegionToLocation implements Function<Region, Location> {
-
-   @Override
-   public Location apply(Region input) {
-      return new LocationBuilder()
-              .description(input.description())
-              .metadata(ImmutableMap.of("selfLink", (Object) checkNotNull(input.selfLink(), "region URI")))
-              .id(input.name())
-              .scope(LocationScope.REGION)
-              .parent(GOOGLE_PROVIDER_LOCATION)
-              .build();
-   }
-}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/ZoneToLocation.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/ZoneToLocation.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/ZoneToLocation.java
deleted file mode 100644
index b4455b9..0000000
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/ZoneToLocation.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.googlecomputeengine.compute.functions;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.GOOGLE_PROVIDER_LOCATION;
-
-import org.jclouds.domain.Location;
-import org.jclouds.domain.LocationBuilder;
-import org.jclouds.domain.LocationScope;
-import org.jclouds.googlecomputeengine.domain.Zone;
-
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableMap;
-
-/**
- * Transforms a google compute domain specific zone to a generic Zone object.
- */
-public class ZoneToLocation implements Function<Zone, Location> {
-
-   @Override
-   public Location apply(Zone input) {
-      return new LocationBuilder()
-              .description(input.description())
-              .metadata(ImmutableMap.of("selfLink", (Object) checkNotNull(input.selfLink(), "zone URI")))
-              .id(input.name())
-              .scope(LocationScope.ZONE)
-              .parent(GOOGLE_PROVIDER_LOCATION)
-              .build();
-   }
-}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/options/GoogleComputeEngineTemplateOptions.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/options/GoogleComputeEngineTemplateOptions.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/options/GoogleComputeEngineTemplateOptions.java
index 9bb670f..c6e5f69 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/options/GoogleComputeEngineTemplateOptions.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/options/GoogleComputeEngineTemplateOptions.java
@@ -36,7 +36,7 @@ import com.google.common.collect.Lists;
 /**
  * Instance options specific to Google Compute Engine.
  */
-public class GoogleComputeEngineTemplateOptions extends TemplateOptions {
+public final class GoogleComputeEngineTemplateOptions extends TemplateOptions {
 
    private Optional<URI> network = Optional.absent();
    private List<Instance.ServiceAccount> serviceAccounts = Lists.newArrayList();

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineHttpApiModule.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineHttpApiModule.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineHttpApiModule.java
index 51e64c0..1f95582 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineHttpApiModule.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineHttpApiModule.java
@@ -18,10 +18,10 @@ package org.jclouds.googlecomputeengine.config;
 
 import static com.google.common.base.Suppliers.compose;
 import static com.google.inject.name.Names.named;
+import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
 
 import java.net.URI;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.inject.Named;
@@ -31,7 +31,6 @@ import org.jclouds.domain.Credentials;
 import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.compute.domain.SlashEncodedIds;
 import org.jclouds.googlecomputeengine.domain.Operation;
-import org.jclouds.googlecomputeengine.domain.Project;
 import org.jclouds.googlecomputeengine.handlers.GoogleComputeEngineErrorHandler;
 import org.jclouds.googlecomputeengine.predicates.GlobalOperationDonePredicate;
 import org.jclouds.googlecomputeengine.predicates.RegionOperationDonePredicate;
@@ -44,8 +43,6 @@ import org.jclouds.http.annotation.ServerError;
 import org.jclouds.json.config.GsonModule.DateAdapter;
 import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
 import org.jclouds.location.Provider;
-import org.jclouds.location.suppliers.ImplicitLocationSupplier;
-import org.jclouds.location.suppliers.implicit.FirstZone;
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.ConfiguresHttpApi;
 import org.jclouds.rest.config.HttpApiModule;
@@ -57,14 +54,10 @@ import com.google.common.base.Splitter;
 import com.google.common.base.Supplier;
 import com.google.common.collect.Iterables;
 import com.google.inject.Provides;
-import com.google.inject.Scopes;
 import com.google.inject.TypeLiteral;
 
-/**
- * Configures the GoogleCompute connection.
- */
 @ConfiguresHttpApi
-public class GoogleComputeEngineHttpApiModule extends HttpApiModule<GoogleComputeEngineApi> {
+public final class GoogleComputeEngineHttpApiModule extends HttpApiModule<GoogleComputeEngineApi> {
    public GoogleComputeEngineHttpApiModule() {
    }
 
@@ -77,7 +70,6 @@ public class GoogleComputeEngineHttpApiModule extends HttpApiModule<GoogleComput
       }).annotatedWith(named("region")).to(RegionOperationDonePredicate.class);
       bind(new TypeLiteral<Predicate<AtomicReference<Operation>>>() {
       }).annotatedWith(named("zone")).to(ZoneOperationDonePredicate.class);
-      bind(ImplicitLocationSupplier.class).to(FirstZone.class).in(Scopes.SINGLETON);
       super.configure();
    }
 
@@ -88,13 +80,21 @@ public class GoogleComputeEngineHttpApiModule extends HttpApiModule<GoogleComput
       bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(GoogleComputeEngineErrorHandler.class);
    }
 
+   @Override
+   protected void installLocations() {
+      install(new GoogleComputeEngineLocationModule());
+   }
+
+   /**
+    * Since this is caching a direct api call, we memoize, but short-circuit on any auth exception. This prevents
+    * excessive errors when things occur in parallel, or as peers on a function graph.
+    */
    @Provides
    @Singleton
-   @UserProject
-   public Supplier<String> supplyProject(@Provider final Supplier<Credentials> creds,
-                                         final GoogleComputeEngineApi api,
-                                         AtomicReference<AuthorizationException> authException,
-                                         @Named(PROPERTY_SESSION_INTERVAL) long seconds) {
+   @UserProject Supplier<String> projectName(@Provider final Supplier<Credentials> creds,
+                                             final GoogleComputeEngineApi api,
+                                             AtomicReference<AuthorizationException> authException,
+                                             @Named(PROPERTY_SESSION_INTERVAL) long seconds) {
       return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
               compose(new Function<Credentials, String>() {
                  public String apply(Credentials in) {
@@ -111,17 +111,15 @@ public class GoogleComputeEngineHttpApiModule extends HttpApiModule<GoogleComput
                           projectName = Iterables.get(Splitter.on("-").split(projectName), 0);
                        }
                     }
-                    Project project = api.getProjectApi().get(projectName);
-                    return project.name();
+                    return api.getProjectApi().get(projectName).name();
                  }
-              }, creds), seconds, TimeUnit.SECONDS);
+              }, creds), seconds, SECONDS);
    }
 
    @Provides
    @Singleton
-   @Named("machineTypeToURI")
-   public Function<String, URI> provideMachineTypeNameToURIFunction(@Provider final Supplier<URI> endpoint,
-                                                                    @UserProject final Supplier<String> userProject) {
+   @Named("machineTypeToURI") Function<String, URI> machineTypeNameToURI(
+         @Provider final Supplier<URI> endpoint, @UserProject final Supplier<String> userProject) {
       return new Function<String, URI>() {
          @Override
          public URI apply(String input) {
@@ -135,43 +133,13 @@ public class GoogleComputeEngineHttpApiModule extends HttpApiModule<GoogleComput
 
    @Provides
    @Singleton
-   @Named("networkToURI")
-   public Function<String, URI> provideNetworkNameToURIFunction(@Provider final Supplier<URI> endpoint,
-                                                                @UserProject final Supplier<String> userProject) {
+   @Named("networkToURI") Function<String, URI> networkNameToURI(@Provider final Supplier<URI> endpoint,
+                                                                 @UserProject final Supplier<String> userProject) {
       return new Function<String, URI>() {
-         @Override
-         public URI apply(String input) {
+         @Override public URI apply(String input) {
             return Uris.uriBuilder(endpoint.get()).appendPath("/projects/").appendPath(userProject.get())
                     .appendPath("/global/networks/").appendPath(input).build();
          }
       };
    }
-
-   @Provides
-   @Singleton
-   @Named("zoneToURI")
-   public Function<String, URI> provideZoneNameToURIFunction(@Provider final Supplier<URI> endpoint,
-                                                             @UserProject final Supplier<String> userProject) {
-      return new Function<String, URI>() {
-         @Override
-         public URI apply(String input) {
-            return Uris.uriBuilder(endpoint.get()).appendPath("/projects/").appendPath(userProject.get())
-                    .appendPath("/zones/").appendPath(input).build();
-         }
-      };
-   }
-
-   @Provides
-   @Singleton
-   @Named("regionToURI")
-   public Function<String, URI> provideRegionNameToURIFunction(@Provider final Supplier<URI> endpoint,
-                                                               @UserProject final Supplier<String> userProject) {
-      return new Function<String, URI>() {
-         @Override
-         public URI apply(String input) {
-            return Uris.uriBuilder(endpoint.get()).appendPath("/projects/").appendPath(userProject.get())
-                    .appendPath("/regions/").appendPath(input).build();
-         }
-      };
-   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineLocationModule.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineLocationModule.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineLocationModule.java
new file mode 100644
index 0000000..03d0e9d
--- /dev/null
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineLocationModule.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.googlecomputeengine.config;
+
+import static com.google.common.base.Suppliers.compose;
+import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
+import static org.jclouds.googlecomputeengine.internal.ListPages.concat;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.collect.Memoized;
+import org.jclouds.domain.Location;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
+import org.jclouds.googlecomputeengine.domain.Region;
+import org.jclouds.location.config.LocationModule;
+import org.jclouds.location.predicates.LocationPredicates;
+import org.jclouds.location.reference.LocationConstants;
+import org.jclouds.location.suppliers.ImplicitLocationSupplier;
+import org.jclouds.location.suppliers.LocationsSupplier;
+import org.jclouds.location.suppliers.RegionIdToURISupplier;
+import org.jclouds.location.suppliers.RegionIdToZoneIdsSupplier;
+import org.jclouds.location.suppliers.RegionIdsSupplier;
+import org.jclouds.location.suppliers.ZoneIdToURISupplier;
+import org.jclouds.location.suppliers.ZoneIdsSupplier;
+import org.jclouds.location.suppliers.all.ZoneToRegionToProviderOrJustProvider;
+import org.jclouds.location.suppliers.derived.RegionIdsFromRegionIdToURIKeySet;
+import org.jclouds.location.suppliers.derived.ZoneIdsFromRegionIdToZoneIdsValues;
+import org.jclouds.location.suppliers.implicit.FirstZone;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.inject.Provides;
+
+/**
+ * This configures dynamic locations from {@link org.jclouds.googlecomputeengine.features.RegionApi#list}. As the only
+ * assignable location for nodes are zones, this module does not directly expose regions. Rather, they can be found by
+ * looking at {@link Location#getParent()} on a zone.
+
+ * <p/> This does not yet support constraining region or zone lists via settings {@linkplain
+ * LocationConstants#PROPERTY_REGIONS} or {@linkplain LocationConstants#PROPERTY_ZONES}.
+ */
+public final class GoogleComputeEngineLocationModule extends LocationModule {
+
+   @Override protected void configure() {
+      super.configure();
+      // Unlike EC2, you cannot default GCE instances to a region. Hence, we constrain to zones.
+      bind(LocationsSupplier.class).to(OnlyZonesLocationSupplier.class);
+      bind(ImplicitLocationSupplier.class).to(FirstZone.class);
+
+      // Region and zones are derived from the same network request to RegionApi.list
+      // Using these suppliers will make that consistent and also cache timeout consistently
+      bind(RegionIdToZoneIdsSupplier.class).to(RegionIdToZoneIdsFromRegionList.class);
+      bind(RegionIdToURISupplier.class).to(RegionIdToURISupplierFromRegionList.class);
+      bind(ZoneIdToURISupplier.class).to(ZoneIdToURIFromRegionList.class);
+      bind(ZoneIdsSupplier.class).to(ZoneIdsFromRegionIdToZoneIdsValues.class);
+      bind(RegionIdsSupplier.class).to(RegionIdsFromRegionIdToURIKeySet.class);
+   }
+
+   /** Retain the metadata tree, including regions, just don't present anything except zones as assignable. */
+   static final class OnlyZonesLocationSupplier implements LocationsSupplier {
+      // This correctly links parents for zone -> region -> provider.
+      private final ZoneToRegionToProviderOrJustProvider delegate;
+
+      @Inject OnlyZonesLocationSupplier(ZoneToRegionToProviderOrJustProvider delegate) {
+         this.delegate = delegate;
+      }
+
+      @Override public Set<? extends Location> get() {
+         return Sets.filter(delegate.get(), LocationPredicates.isZone());
+      }
+   }
+
+   /**
+    * Since this is caching a direct api call, we memoize, but short-circuit on any auth exception. This prevents
+    * excessive errors when things occur in parallel, or as peers on a function graph.
+    */
+   @Provides @Singleton @Memoized Supplier<List<Region>> regions(@UserProject Supplier<String> project,
+         final GoogleComputeEngineApi api, AtomicReference<AuthorizationException> authException,
+         @Named(PROPERTY_SESSION_INTERVAL) long seconds) {
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier
+            .create(authException, compose(new Function<String, List<Region>>() {
+               public List<Region> apply(String project) {
+                  return ImmutableList.copyOf(concat(api.getRegionApi(project).list()));
+               }
+            }, project), seconds, TimeUnit.SECONDS);
+   }
+
+   @Provides @Singleton @Memoized Supplier<Map<URI, String>> selfLinkToNames(
+         AtomicReference<AuthorizationException> authException, @Memoized Supplier<List<Region>> regions,
+         @Named(PROPERTY_SESSION_INTERVAL) long seconds) {
+      return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier
+            .create(authException, compose(new Function<List<Region>, Map<URI, String>>() {
+               public Map<URI, String> apply(List<Region> regions) {
+                  ImmutableMap.Builder<URI, String> selfLinkToName = ImmutableMap.builder();
+                  for (Region region : regions) {
+                     selfLinkToName.put(region.selfLink(), region.name());
+                     for (URI zoneSelfLink : region.zones()) {
+                        selfLinkToName.put(zoneSelfLink, toName(zoneSelfLink));
+                     }
+                  }
+                  return selfLinkToName.build();
+               }
+            }, regions), seconds, TimeUnit.SECONDS);
+   }
+
+   static final class RegionIdToZoneIdsFromRegionList implements RegionIdToZoneIdsSupplier {
+      private final Supplier<List<Region>> regions;
+
+      @Inject RegionIdToZoneIdsFromRegionList(@Memoized Supplier<List<Region>> regions) {
+         this.regions = regions;
+      }
+
+      @Override public Map<String, Supplier<Set<String>>> get() {
+         ImmutableMap.Builder<String, Supplier<Set<String>>> result = ImmutableMap.builder();
+         for (org.jclouds.googlecomputeengine.domain.Region region : regions.get()) {
+            ImmutableSet.Builder<String> zoneIds = ImmutableSet.builder();
+            for (URI uri : region.zones()) {
+               zoneIds.add(toName(uri));
+            }
+            result.put(region.name(), Suppliers.<Set<String>>ofInstance(zoneIds.build()));
+         }
+         return result.build();
+      }
+   }
+
+   static final class RegionIdToURISupplierFromRegionList implements RegionIdToURISupplier {
+      private final Supplier<List<Region>> regions;
+
+      @Inject RegionIdToURISupplierFromRegionList(@Memoized Supplier<List<Region>> regions) {
+         this.regions = regions;
+      }
+
+      @Override public Map<String, Supplier<URI>> get() {
+         ImmutableMap.Builder<String, Supplier<URI>> result = ImmutableMap.builder();
+         for (org.jclouds.googlecomputeengine.domain.Region region : regions.get()) {
+            result.put(region.name(), Suppliers.ofInstance(region.selfLink()));
+         }
+         return result.build();
+      }
+   }
+
+   static final class ZoneIdToURIFromRegionList implements ZoneIdToURISupplier {
+      private final Supplier<List<Region>> regions;
+
+      @Inject ZoneIdToURIFromRegionList(@Memoized Supplier<List<Region>> regions) {
+         this.regions = regions;
+      }
+
+      @Override public Map<String, Supplier<URI>> get() {
+         ImmutableMap.Builder<String, Supplier<URI>> result = ImmutableMap.builder();
+         for (Region region : regions.get()) {
+            for (URI input : region.zones()) {
+               result.put(toName(input), Suppliers.ofInstance(input));
+            }
+         }
+         return result.build();
+      }
+   }
+
+   private static String toName(URI link) {
+      String path = link.getPath();
+      return path.substring(path.lastIndexOf('/') + 1);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineParserModule.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineParserModule.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineParserModule.java
index c0a7883..815ccf1 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineParserModule.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/GoogleComputeEngineParserModule.java
@@ -55,7 +55,7 @@ public final class GoogleComputeEngineParserModule extends AbstractModule {
       bind(GsonModule.DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class);
    }
 
-   @Provides @Singleton public Map<Type, Object> typeAdapters() {
+   @Provides @Singleton Map<Type, Object> typeAdapters() {
       return new ImmutableMap.Builder<Type, Object>()
             .put(InstanceTemplate.class, new InstanceTemplateTypeAdapter())
             .put(FirewallOptions.class, new FirewallOptionsTypeAdapter())
@@ -63,11 +63,11 @@ public final class GoogleComputeEngineParserModule extends AbstractModule {
    }
 
    // TODO: change jclouds core to use collaborative set bindings
-   @Provides @Singleton public Set<TypeAdapterFactory> typeAdapterFactories() {
+   @Provides @Singleton Set<TypeAdapterFactory> typeAdapterFactories() {
       return ImmutableSet.<TypeAdapterFactory>of(new MetadataTypeAdapter());
    }
 
-   private static class InstanceTemplateTypeAdapter implements JsonSerializer<InstanceTemplate> {
+   private static final class InstanceTemplateTypeAdapter implements JsonSerializer<InstanceTemplate> {
 
       @Override public JsonElement serialize(InstanceTemplate src, Type typeOfSrc, JsonSerializationContext context) {
          InstanceTemplateInternal template = new InstanceTemplateInternal(src);
@@ -96,7 +96,7 @@ public final class GoogleComputeEngineParserModule extends AbstractModule {
          return instance;
       }
 
-      private static class InstanceTemplateInternal extends InstanceTemplate {
+      private static final class InstanceTemplateInternal extends InstanceTemplate {
          private InstanceTemplateInternal(InstanceTemplate template) {
             machineType(template.machineType());
             name(template.name());
@@ -108,11 +108,7 @@ public final class GoogleComputeEngineParserModule extends AbstractModule {
       }
    }
 
-   private static class MetadataTypeAdapter extends SubtypeAdapterFactory<Metadata> {
-
-      private MetadataTypeAdapter() {
-         super(Metadata.class);
-      }
+   private static final class MetadataTypeAdapter extends TypeAdapter<Metadata> implements TypeAdapterFactory {
 
       @Override public void write(JsonWriter out, Metadata src) throws IOException {
          out.beginObject();
@@ -167,9 +163,16 @@ public final class GoogleComputeEngineParserModule extends AbstractModule {
          in.endObject();
          return Metadata.create(fingerprint, builder.build());
       }
+
+      @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+         if (!(Metadata.class.isAssignableFrom(typeToken.getRawType()))) {
+            return null;
+         }
+         return (TypeAdapter<T>) this;
+      }
    }
 
-   private static class FirewallOptionsTypeAdapter implements JsonSerializer<FirewallOptions> {
+   private static final class FirewallOptionsTypeAdapter implements JsonSerializer<FirewallOptions> {
 
       @Override public JsonElement serialize(FirewallOptions src, Type typeOfSrc, JsonSerializationContext context) {
          JsonObject firewall = new JsonObject();
@@ -199,7 +202,7 @@ public final class GoogleComputeEngineParserModule extends AbstractModule {
       }
    }
 
-   private static class RouteOptionsTypeAdapter implements JsonSerializer<RouteOptions> {
+   private static final class RouteOptionsTypeAdapter implements JsonSerializer<RouteOptions> {
 
       @Override public JsonElement serialize(RouteOptions src, Type typeOfSrc, JsonSerializationContext context) {
          JsonObject route = new JsonObject();
@@ -244,19 +247,4 @@ public final class GoogleComputeEngineParserModule extends AbstractModule {
       }
       return array;
    }
-
-   private abstract static class SubtypeAdapterFactory<T> extends TypeAdapter<T> implements TypeAdapterFactory {
-      private final Class<T> baseClass;
-
-      private SubtypeAdapterFactory(Class<T> baseClass) {
-         this.baseClass = baseClass;
-      }
-
-      @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
-         if (!(baseClass.isAssignableFrom(typeToken.getRawType()))) {
-            return null;
-         }
-         return (TypeAdapter<T>) this;
-      }
-   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/RegionOperationDonePredicate.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/RegionOperationDonePredicate.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/RegionOperationDonePredicate.java
index 1eb3f4d..65c0723 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/RegionOperationDonePredicate.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/RegionOperationDonePredicate.java
@@ -28,7 +28,6 @@ import org.jclouds.collect.Memoized;
 import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.config.UserProject;
 import org.jclouds.googlecomputeengine.domain.Operation;
-import org.jclouds.googlecomputeengine.domain.Region;
 
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
@@ -36,19 +35,19 @@ import com.google.common.base.Supplier;
 public final class RegionOperationDonePredicate implements Predicate<AtomicReference<Operation>> {
    private final GoogleComputeEngineApi api;
    private final Supplier<String> project;
-   private final Supplier<Map<URI, Region>> regions;
+   private final Supplier<Map<URI, String>> selfLinkToName;
 
    @Inject RegionOperationDonePredicate(GoogleComputeEngineApi api, @UserProject Supplier<String> project,
-                                @Memoized Supplier<Map<URI, Region>> regions) {
+         @Memoized Supplier<Map<URI, String>> selfLinkToName) {
       this.api = api;
       this.project = project;
-      this.regions = regions;
+      this.selfLinkToName = selfLinkToName;
    }
 
    @Override public boolean apply(AtomicReference<Operation> input) {
       checkNotNull(input.get(), "input");
       URI region = checkNotNull(input.get().region(), "region of %s", input.get());
-      String locationId = checkNotNull(regions.get().get(region), "location of %s", region).id();
+      String locationId = checkNotNull(selfLinkToName.get().get(region), "location of %s", region);
       Operation current = api.getRegionOperationApi(project.get(), locationId).get(input.get().name());
       switch (current.status()) {
          case DONE:

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/867c785b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/ZoneOperationDonePredicate.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/ZoneOperationDonePredicate.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/ZoneOperationDonePredicate.java
index 9c727ed..0081d30 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/ZoneOperationDonePredicate.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/ZoneOperationDonePredicate.java
@@ -23,7 +23,6 @@ import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.jclouds.collect.Memoized;
-import org.jclouds.domain.Location;
 import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.config.UserProject;
 import org.jclouds.googlecomputeengine.domain.Operation;
@@ -39,19 +38,19 @@ public final class ZoneOperationDonePredicate implements Predicate<AtomicReferen
 
    private final GoogleComputeEngineApi api;
    private final Supplier<String> project;
-   private final Supplier<Map<URI, ? extends Location>> zones;
+   private final Supplier<Map<URI, String>> selfLinkToName;
 
    @Inject ZoneOperationDonePredicate(GoogleComputeEngineApi api, @UserProject Supplier<String> project,
-         @Memoized Supplier<Map<URI, ? extends Location>> zones) {
+         @Memoized Supplier<Map<URI, String>> selfLinkToName) {
       this.api = api;
       this.project = project;
-      this.zones = zones;
+      this.selfLinkToName = selfLinkToName;
    }
 
    @Override public boolean apply(AtomicReference<Operation> input) {
       checkNotNull(input.get(), "input");
       URI zone = checkNotNull(input.get().zone(), "zone of %s", input.get());
-      String locationId = checkNotNull(zones.get().get(zone), "location of %s", zone).getId();
+      String locationId = checkNotNull(selfLinkToName.get().get(zone), "location of %s", zone);
       Operation current = api.getZoneOperationApi(project.get(), locationId).get(input.get().name());
       switch (current.status()) {
          case DONE: