You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ab...@apache.org on 2013/10/03 23:16:07 UTC

[1/2] JCLOUDS-312. Add SecurityGroupExtension for GCE.

Updated Branches:
  refs/heads/master 7295f34e5 -> 97c911ca3


http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/FirewallApiLiveTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/FirewallApiLiveTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/FirewallApiLiveTest.java
index 8c550b7..ca18883 100644
--- a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/FirewallApiLiveTest.java
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/FirewallApiLiveTest.java
@@ -17,7 +17,6 @@
 package org.jclouds.googlecomputeengine.features;
 
 import static com.google.common.collect.Iterables.getOnlyElement;
-import static org.jclouds.googlecomputeengine.domain.Firewall.Rule.IPProtocol;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 
@@ -28,6 +27,7 @@ import org.jclouds.googlecomputeengine.domain.Firewall;
 import org.jclouds.googlecomputeengine.internal.BaseGoogleComputeEngineApiLiveTest;
 import org.jclouds.googlecomputeengine.options.FirewallOptions;
 import org.jclouds.googlecomputeengine.options.ListOptions;
+import org.jclouds.net.domain.IpProtocol;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableSet;
@@ -57,7 +57,7 @@ public class FirewallApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
       FirewallOptions firewall = new FirewallOptions()
               .addAllowedRule(
                       Firewall.Rule.builder()
-                              .IPProtocol(IPProtocol.TCP)
+                              .IpProtocol(IpProtocol.TCP)
                               .addPort(22).build())
               .addSourceRange("10.0.0.0/8")
               .addSourceTag("tag1")
@@ -79,7 +79,7 @@ public class FirewallApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
               .addTargetTag("tag2")
               .allowedRules(ImmutableSet.of(
                       Firewall.Rule.builder()
-                              .IPProtocol(IPProtocol.TCP)
+                              .IpProtocol(IpProtocol.TCP)
                               .addPort(23)
                               .build()));
 
@@ -96,11 +96,11 @@ public class FirewallApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
               .network(getNetworkUrl(userProject.get(), FIREWALL_NETWORK_NAME))
               .allowedRules(ImmutableSet.of(
                       Firewall.Rule.builder()
-                              .IPProtocol(IPProtocol.TCP)
+                              .IpProtocol(IpProtocol.TCP)
                               .addPort(22)
                               .build(),
                       Firewall.Rule.builder()
-                              .IPProtocol(IPProtocol.TCP)
+                              .IpProtocol(IpProtocol.TCP)
                               .addPort(23)
                               .build()))
               .addSourceRange("10.0.0.0/8")
@@ -119,11 +119,11 @@ public class FirewallApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
               .network(getNetworkUrl(userProject.get(), FIREWALL_NETWORK_NAME))
               .allowedRules(ImmutableSet.of(
                       Firewall.Rule.builder()
-                              .IPProtocol(IPProtocol.TCP)
+                              .IpProtocol(IpProtocol.TCP)
                               .addPort(22)
                               .build(),
                       Firewall.Rule.builder()
-                              .IPProtocol(IPProtocol.TCP)
+                              .IpProtocol(IpProtocol.TCP)
                               .addPort(23)
                               .build()))
               .addSourceRange("10.0.0.0/8")

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/functions/CreateNetworkIfNeededTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/functions/CreateNetworkIfNeededTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/functions/CreateNetworkIfNeededTest.java
new file mode 100644
index 0000000..4bbff5e
--- /dev/null
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/functions/CreateNetworkIfNeededTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.functions;
+
+import static com.google.common.base.Optional.fromNullable;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
+import org.jclouds.googlecomputeengine.domain.Network;
+import org.jclouds.googlecomputeengine.domain.Operation;
+import org.jclouds.googlecomputeengine.domain.internal.NetworkAndAddressRange;
+import org.jclouds.googlecomputeengine.features.GlobalOperationApi;
+import org.jclouds.googlecomputeengine.features.NetworkApi;
+import org.jclouds.googlecomputeengine.predicates.GlobalOperationDonePredicate;
+import org.jclouds.http.HttpResponse;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+
+/**
+ * @author Andrew Bayer
+ */
+public class CreateNetworkIfNeededTest {
+
+   @Test
+   public void testApply() {
+      final GoogleComputeEngineApi api = createMock(GoogleComputeEngineApi.class);
+      final NetworkApi nwApi = createMock(NetworkApi.class);
+      final GlobalOperationApi globalApi = createMock(GlobalOperationApi.class);
+
+      Network network = Network.builder().IPv4Range("0.0.0.0/0")
+              .id("abcd").name("this-network")
+              .selfLink(URI.create("https://www.googleapis.com/compute/v1beta15/projects/myproject/global/networks/this-network"))
+              .build();
+
+      Operation createOp = createMock(Operation.class);
+
+      final Supplier<String> userProject = new Supplier<String>() {
+         @Override
+         public String get() {
+            return "myproject";
+         }
+      };
+
+      expect(api.getNetworkApiForProject(userProject.get())).andReturn(nwApi).atLeastOnce();
+      expect(api.getGlobalOperationApiForProject(userProject.get())).andReturn(globalApi).atLeastOnce();
+
+      expect(nwApi.createInIPv4Range("this-network", "0.0.0.0/0"))
+              .andReturn(createOp);
+      expect(globalApi.get("create-op")).andReturn(createOp);
+      expect(nwApi.get("this-network")).andReturn(network);
+
+      expect(createOp.getName()).andReturn("create-op");
+      expect(createOp.getStatus()).andReturn(Operation.Status.DONE);
+      expect(createOp.getHttpError()).andReturn(fromNullable((HttpResponse)null));
+      replay(api, nwApi, createOp, globalApi);
+
+      NetworkAndAddressRange input = new NetworkAndAddressRange("this-network", "0.0.0.0/0", null);
+
+      GlobalOperationDonePredicate pred = new GlobalOperationDonePredicate(api, userProject);
+
+      CreateNetworkIfNeeded creator = new CreateNetworkIfNeeded(api, userProject, pred, 100l, 100l);
+
+      assertEquals(creator.apply(input), network);
+
+      verify(api, nwApi, globalApi, createOp);
+   }
+
+   @Test
+   public void testApplyWithGateway() {
+      final GoogleComputeEngineApi api = createMock(GoogleComputeEngineApi.class);
+      final NetworkApi nwApi = createMock(NetworkApi.class);
+      final GlobalOperationApi globalApi = createMock(GlobalOperationApi.class);
+
+      Network network = Network.builder().IPv4Range("0.0.0.0/0")
+              .id("abcd").name("this-network").gatewayIPv4("1.2.3.4")
+              .selfLink(URI.create("https://www.googleapis.com/compute/v1beta15/projects/myproject/global/networks/this-network"))
+              .build();
+
+      Operation createOp = createMock(Operation.class);
+
+      final Supplier<String> userProject = new Supplier<String>() {
+         @Override
+         public String get() {
+            return "myproject";
+         }
+      };
+
+      expect(api.getNetworkApiForProject(userProject.get())).andReturn(nwApi).atLeastOnce();
+      expect(api.getGlobalOperationApiForProject(userProject.get())).andReturn(globalApi).atLeastOnce();
+
+      expect(nwApi.createInIPv4RangeWithGateway("this-network", "0.0.0.0/0", "1.2.3.4"))
+              .andReturn(createOp);
+      expect(globalApi.get("create-op")).andReturn(createOp);
+      expect(nwApi.get("this-network")).andReturn(network);
+
+      expect(createOp.getName()).andReturn("create-op");
+      expect(createOp.getStatus()).andReturn(Operation.Status.DONE);
+      expect(createOp.getHttpError()).andReturn(fromNullable((HttpResponse)null));
+      replay(api, nwApi, createOp, globalApi);
+
+      NetworkAndAddressRange input = new NetworkAndAddressRange("this-network", "0.0.0.0/0", "1.2.3.4");
+
+      GlobalOperationDonePredicate pred = new GlobalOperationDonePredicate(api, userProject);
+
+      CreateNetworkIfNeeded creator = new CreateNetworkIfNeeded(api, userProject, pred, 100l, 100l);
+
+      assertEquals(creator.apply(input), network);
+
+      verify(api, nwApi, globalApi, createOp);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseFirewallListTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseFirewallListTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseFirewallListTest.java
index a90f17b..1bbf753 100644
--- a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseFirewallListTest.java
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseFirewallListTest.java
@@ -26,6 +26,7 @@ import org.jclouds.googlecomputeengine.domain.Firewall;
 import org.jclouds.googlecomputeengine.domain.ListPage;
 import org.jclouds.googlecomputeengine.domain.Resource;
 import org.jclouds.googlecomputeengine.internal.BaseGoogleComputeEngineParseTest;
+import org.jclouds.net.domain.IpProtocol;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableSet;
@@ -61,7 +62,7 @@ public class ParseFirewallListTest extends BaseGoogleComputeEngineParseTest<List
                               ".com/compute/v1beta15/projects/google/global/networks/default"))
                       .addSourceRange("0.0.0.0/0")
                       .addAllowed(Firewall.Rule.builder()
-                              .IPProtocol(Firewall.Rule.IPProtocol.TCP)
+                              .IpProtocol(IpProtocol.TCP)
                               .addPort(22).build())
                       .build()
               ))

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseFirewallTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseFirewallTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseFirewallTest.java
index 187380f..49012fb 100644
--- a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseFirewallTest.java
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseFirewallTest.java
@@ -16,8 +16,6 @@
  */
 package org.jclouds.googlecomputeengine.parse;
 
-import static org.jclouds.googlecomputeengine.domain.Firewall.Rule.IPProtocol;
-
 import java.net.URI;
 
 import javax.ws.rs.Consumes;
@@ -26,6 +24,7 @@ import javax.ws.rs.core.MediaType;
 import org.jclouds.date.internal.SimpleDateFormatDateService;
 import org.jclouds.googlecomputeengine.domain.Firewall;
 import org.jclouds.googlecomputeengine.internal.BaseGoogleComputeEngineParseTest;
+import org.jclouds.net.domain.IpProtocol;
 import org.testng.annotations.Test;
 
 /**
@@ -51,13 +50,13 @@ public class ParseFirewallTest extends BaseGoogleComputeEngineParseTest<Firewall
               .network(URI.create("https://www.googleapis.com/compute/v1beta15/projects/myproject/global/networks/jclouds-test"))
               .addSourceRange("10.0.0.0/8")
               .addAllowed(Firewall.Rule.builder()
-                      .IPProtocol(IPProtocol.TCP)
+                      .IpProtocol(IpProtocol.TCP)
                       .addPortRange(1, 65535).build())
               .addAllowed(Firewall.Rule.builder()
-                      .IPProtocol(IPProtocol.UDP)
+                      .IpProtocol(IpProtocol.UDP)
                       .addPortRange(1, 65535).build())
               .addAllowed(Firewall.Rule.builder()
-                      .IPProtocol(IPProtocol.ICMP).build())
+                      .IpProtocol(IpProtocol.ICMP).build())
               .build();
 
    }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/predicates/NetworkFirewallPredicatesTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/predicates/NetworkFirewallPredicatesTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/predicates/NetworkFirewallPredicatesTest.java
new file mode 100644
index 0000000..a7c43f2
--- /dev/null
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/predicates/NetworkFirewallPredicatesTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.predicates;
+
+import static org.jclouds.googlecomputeengine.compute.functions.FirewallToIpPermissionTest.fwForTest;
+import static org.jclouds.googlecomputeengine.predicates.NetworkFirewallPredicates.equalsIpPermission;
+import static org.jclouds.googlecomputeengine.predicates.NetworkFirewallPredicates.hasPortRange;
+import static org.jclouds.googlecomputeengine.predicates.NetworkFirewallPredicates.hasProtocol;
+import static org.jclouds.googlecomputeengine.predicates.NetworkFirewallPredicates.hasSourceRange;
+import static org.jclouds.googlecomputeengine.predicates.NetworkFirewallPredicates.hasSourceTag;
+import static org.jclouds.googlecomputeengine.predicates.NetworkFirewallPredicates.providesIpPermission;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.Date;
+
+import org.jclouds.googlecomputeengine.domain.Firewall;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Range;
+
+@Test(groups = "unit")
+public class NetworkFirewallPredicatesTest {
+
+   public static Firewall getFwForTestSourceTags() {
+      Firewall.Builder builder = Firewall.builder();
+
+      builder.network(URI.create("https://www.googleapis.com/compute/v1beta15/projects/myproject/global/networks/jclouds-test"));
+      builder.selfLink(URI.create("https://www.googleapis.com/compute/v1beta15/projects/myproject/global/firewalls/jclouds-test"));
+      builder.addSourceTag("tag-1");
+      builder.addAllowed(Firewall.Rule.builder().IpProtocol(IpProtocol.TCP)
+              .addPortRange(1, 10).build());
+      builder.addAllowed(Firewall.Rule.builder().IpProtocol(IpProtocol.TCP)
+              .addPort(33).build());
+      builder.addAllowed(Firewall.Rule.builder().IpProtocol(IpProtocol.ICMP).build());
+      builder.id("abcd");
+      builder.creationTimestamp(new Date());
+      builder.name("jclouds-test");
+
+      return builder.build();
+   }
+
+   public static Firewall getFwForTestSourceTagsExact() {
+      Firewall.Builder builder = Firewall.builder();
+
+      builder.network(URI.create("https://www.googleapis.com/compute/v1beta15/projects/myproject/global/networks/jclouds-test"));
+      builder.selfLink(URI.create("https://www.googleapis.com/compute/v1beta15/projects/myproject/global/firewalls/jclouds-test"));
+      builder.addSourceTag("tag-1");
+      builder.addAllowed(Firewall.Rule.builder().IpProtocol(IpProtocol.TCP)
+              .addPortRange(1, 10).build());
+      builder.id("abcd");
+      builder.creationTimestamp(new Date());
+      builder.name("jclouds-test");
+
+      return builder.build();
+   }
+
+   @Test
+   public void testHasProtocol() {
+      assertTrue(hasProtocol(IpProtocol.TCP).apply(fwForTest()),
+              "Firewall " + fwForTest() + " should contain a TCP rule.");
+   }
+
+   @Test
+   public void testHasProtocolFails() {
+      assertFalse(hasProtocol(IpProtocol.UDP).apply(fwForTest()),
+              "Firewall " + fwForTest() + " should NOT contain a UDP rule.");
+   }
+
+   @Test
+   public void testHasPortRange() {
+      assertTrue(hasPortRange(Range.closed(2, 9)).apply(fwForTest()),
+              "Firewall " + fwForTest() + " should contain the port range 2-9.");
+   }
+
+   @Test
+   public void testHasPortRangeFails() {
+      assertFalse(hasPortRange(Range.closed(11, 15)).apply(fwForTest()),
+              "Firewall " + fwForTest() + " should NOT contain the port range 11-15.");
+   }
+
+   @Test
+   public void testHasSourceTag() {
+      assertTrue(hasSourceTag("tag-1").apply(getFwForTestSourceTags()),
+              "Firewall " + getFwForTestSourceTags() + " should contain the source tag 'tag-1'.");
+   }
+
+   @Test
+   public void testHasSourceTagFails() {
+      assertFalse(hasSourceTag("tag-1").apply(fwForTest()),
+              "Firewall " + fwForTest() + " should NOT contain the source tag 'tag-1'.");
+   }
+
+   @Test
+   public void testHasSourceRange() {
+      assertTrue(hasSourceRange("0.0.0.0/0").apply(fwForTest()),
+              "Firewall " + fwForTest() + " should contain the source range '0.0.0.0/0'.");
+   }
+
+   @Test
+   public void testHasSourceRangeFails() {
+      assertFalse(hasSourceRange("0.0.0.0/0").apply(getFwForTestSourceTags()),
+              "Firewall " + getFwForTestSourceTags() + " should NOT contain the source range '0.0.0.0/0'.");
+   }
+
+   @Test
+   public void testEqualsIpPermission() {
+      IpPermission perm = IpPermission.builder().groupId("tag-1")
+              .fromPort(1).toPort(10).ipProtocol(IpProtocol.TCP).build();
+
+      assertTrue(equalsIpPermission(perm).apply(getFwForTestSourceTagsExact()),
+              "Firewall " + getFwForTestSourceTagsExact() + " should match IpPermission " + perm + " but does not.");
+   }
+
+   @Test
+   public void testEqualsIpPermissionFails() {
+      IpPermission perm = IpPermission.builder().groupId("tag-1")
+              .fromPort(1).toPort(10).ipProtocol(IpProtocol.TCP).build();
+
+      assertFalse(equalsIpPermission(perm).apply(getFwForTestSourceTags()),
+              "Firewall " + getFwForTestSourceTags() + " should not match IpPermission " + perm + " but does.");
+   }
+
+   @Test
+   public void testProvidesIpPermission() {
+      IpPermission perm = IpPermission.builder().groupId("tag-1")
+              .fromPort(1).toPort(10).ipProtocol(IpProtocol.TCP).build();
+
+      assertTrue(providesIpPermission(perm).apply(getFwForTestSourceTagsExact()),
+              "Firewall " + getFwForTestSourceTagsExact() + " should provide IpPermission " + perm + " but does not.");
+
+      assertTrue(providesIpPermission(perm).apply(getFwForTestSourceTags()),
+              "Firewall " + getFwForTestSourceTags() + " should inexactly provide IpPermission " + perm + " but does not.");
+   }
+
+   @Test
+   public void testProvidesIpPermissionFails() {
+      IpPermission perm = IpPermission.builder().groupId("tag-1")
+              .fromPort(1).toPort(10).ipProtocol(IpProtocol.TCP).build();
+
+      assertFalse(providesIpPermission(perm).apply(fwForTest()),
+              "Firewall " + fwForTest() + " should not provide IpPermission " + perm + " but does.");
+   }
+}
+


[2/2] git commit: JCLOUDS-312. Add SecurityGroupExtension for GCE.

Posted by ab...@apache.org.
JCLOUDS-312. Add SecurityGroupExtension for GCE.

Note - there is no GoogleComputEngineSecurityGroupExtensionExpectTest,
due to it being more or less redundant given that we're not making any
new API calls.


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

Branch: refs/heads/master
Commit: 97c911ca3807bb3c1772a5871f71ee626291df9d
Parents: 7295f34
Author: Andrew Bayer <an...@gmail.com>
Authored: Mon Sep 30 11:54:47 2013 -0700
Committer: Andrew Bayer <an...@gmail.com>
Committed: Tue Oct 1 14:50:55 2013 -0700

----------------------------------------------------------------------
 google-compute-engine/pom.xml                   |   3 +
 ...GoogleComputeEngineServiceContextModule.java |  43 ++-
 ...ogleComputeEngineSecurityGroupExtension.java | 340 +++++++++++++++++++
 .../functions/FirewallToIpPermission.java       |  89 +++++
 .../functions/NetworkToSecurityGroup.java       |  84 +++++
 .../compute/loaders/FindNetworkOrCreate.java    |  65 ++++
 ...desWithGroupEncodedIntoNameThenAddToSet.java |  34 +-
 .../config/GoogleComputeEngineParserModule.java |   5 +-
 .../googlecomputeengine/domain/Firewall.java    |  42 +--
 .../googlecomputeengine/domain/Project.java     |   2 +-
 .../domain/internal/NetworkAndAddressRange.java |  91 +++++
 .../functions/CreateNetworkIfNeeded.java        | 101 ++++++
 .../GlobalOperationDonePredicate.java           |   2 +-
 .../predicates/NetworkFirewallPredicates.java   | 123 +++++++
 ...uteEngineSecurityGroupExtensionLiveTest.java |  31 ++
 .../functions/FirewallToIpPermissionTest.java   |  93 +++++
 .../functions/NetworkToSecurityGroupTest.java   |  94 +++++
 .../loaders/FindNetworkOrCreateTest.java        | 144 ++++++++
 .../features/FirewallApiExpectTest.java         |   8 +-
 .../features/FirewallApiLiveTest.java           |  14 +-
 .../functions/CreateNetworkIfNeededTest.java    | 133 ++++++++
 .../parse/ParseFirewallListTest.java            |   3 +-
 .../parse/ParseFirewallTest.java                |   9 +-
 .../NetworkFirewallPredicatesTest.java          | 162 +++++++++
 24 files changed, 1641 insertions(+), 74 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/pom.xml
----------------------------------------------------------------------
diff --git a/google-compute-engine/pom.xml b/google-compute-engine/pom.xml
index 375192c..de8d759 100644
--- a/google-compute-engine/pom.xml
+++ b/google-compute-engine/pom.xml
@@ -114,6 +114,9 @@
                                     <goal>test</goal>
                                 </goals>
                                 <configuration>
+                                  <includes>
+                                    <include>**/GoogleComputeEngineSecurityGroupExtensionLiveTest.java</include>
+                                  </includes>
                                     <systemPropertyVariables>
                                         <test.google-compute-engine.identity>${test.google-compute-engine.identity}
                                         </test.google-compute-engine.identity>

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/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 1e3580b..b726f11 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
@@ -35,32 +35,43 @@ import org.jclouds.compute.ComputeServiceAdapter;
 import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
 import org.jclouds.compute.domain.Hardware;
 import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.SecurityGroup;
 import org.jclouds.compute.extensions.ImageExtension;
+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.extensions.GoogleComputeEngineSecurityGroupExtension;
 import org.jclouds.googlecomputeengine.compute.functions.BuildInstanceMetadata;
+import org.jclouds.googlecomputeengine.compute.functions.FirewallToIpPermission;
 import org.jclouds.googlecomputeengine.compute.functions.GoogleComputeEngineImageToImage;
 import org.jclouds.googlecomputeengine.compute.functions.InstanceInZoneToNodeMetadata;
 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.loaders.FindNetworkOrCreate;
 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.InstanceInZone;
 import org.jclouds.googlecomputeengine.domain.MachineTypeInZone;
+import org.jclouds.googlecomputeengine.domain.Network;
 import org.jclouds.googlecomputeengine.domain.Region;
 import org.jclouds.googlecomputeengine.domain.Zone;
+import org.jclouds.googlecomputeengine.domain.internal.NetworkAndAddressRange;
+import org.jclouds.googlecomputeengine.functions.CreateNetworkIfNeeded;
+import org.jclouds.net.domain.IpPermission;
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
 
@@ -69,6 +80,9 @@ import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Injector;
 import com.google.inject.Provides;
@@ -105,6 +119,12 @@ public class GoogleComputeEngineServiceContextModule
       bind(new TypeLiteral<Function<Zone, Location>>() {})
               .to(ZoneToLocation.class);
 
+      bind(new TypeLiteral<Function<Firewall, Iterable<IpPermission>>>() {})
+              .to(FirewallToIpPermission.class);
+
+      bind(new TypeLiteral<Function<Network, SecurityGroup>>() {})
+              .to(NetworkToSecurityGroup.class);
+
       bind(new TypeLiteral<Function<TemplateOptions, ImmutableMap.Builder<String, String>>>() {})
               .to(BuildInstanceMetadata.class);
 
@@ -121,6 +141,15 @@ public class GoogleComputeEngineServiceContextModule
 
       bind(new TypeLiteral<Predicate<String>>() {}).to(AllNodesInGroupTerminated.class);
 
+      bind(new TypeLiteral<Function<NetworkAndAddressRange, Network>>() {})
+              .to(CreateNetworkIfNeeded.class);
+
+      bind(new TypeLiteral<CacheLoader<NetworkAndAddressRange, Network>>() {})
+              .to(FindNetworkOrCreate.class);
+
+      bind(new TypeLiteral<SecurityGroupExtension>() {})
+              .to(GoogleComputeEngineSecurityGroupExtension.class);
+
       bind(PrioritizeCredentialsFromTemplate.class).to(UseNodeCredentialsButOverrideFromTemplate.class);
 
       install(new LocationsFromComputeServiceAdapterModule<InstanceInZone, MachineTypeInZone, Image, Zone>() {});
@@ -131,7 +160,7 @@ public class GoogleComputeEngineServiceContextModule
    @Singleton
    @Memoized
    public Supplier<Map<URI, ? extends org.jclouds.compute.domain.Image>> provideImagesMap(
-           AtomicReference<AuthorizationException> authException,
+           AtomicReference <AuthorizationException> authException,
            final Supplier<Set<? extends org.jclouds.compute.domain.Image>> images,
            @Named(PROPERTY_SESSION_INTERVAL) long seconds) {
       return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
@@ -219,11 +248,23 @@ public class GoogleComputeEngineServiceContextModule
               seconds, TimeUnit.SECONDS);
    }
 
+   @Provides
+   @Singleton
+   protected LoadingCache<NetworkAndAddressRange, Network> networkMap(
+           CacheLoader<NetworkAndAddressRange, Network> in) {
+      return CacheBuilder.newBuilder().build(in);
+   }
+
    @Override
    protected Optional<ImageExtension> provideImageExtension(Injector i) {
       return Optional.absent();
    }
 
+   @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()

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/extensions/GoogleComputeEngineSecurityGroupExtension.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/extensions/GoogleComputeEngineSecurityGroupExtension.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/extensions/GoogleComputeEngineSecurityGroupExtension.java
new file mode 100644
index 0000000..2ceccef
--- /dev/null
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/extensions/GoogleComputeEngineSecurityGroupExtension.java
@@ -0,0 +1,340 @@
+/*
+ * 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.extensions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.OPERATION_COMPLETE_INTERVAL;
+import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.OPERATION_COMPLETE_TIMEOUT;
+import static org.jclouds.googlecomputeengine.compute.strategy.CreateNodesWithGroupEncodedIntoNameThenAddToSet.DEFAULT_INTERNAL_NETWORK_RANGE;
+import static org.jclouds.googlecomputeengine.predicates.NetworkFirewallPredicates.equalsIpPermission;
+import static org.jclouds.googlecomputeengine.predicates.NetworkFirewallPredicates.providesIpPermission;
+import static org.jclouds.util.Predicates2.retry;
+
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.domain.Location;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
+import org.jclouds.googlecomputeengine.config.UserProject;
+import org.jclouds.googlecomputeengine.domain.Firewall;
+import org.jclouds.googlecomputeengine.domain.Instance;
+import org.jclouds.googlecomputeengine.domain.Instance.NetworkInterface;
+import org.jclouds.googlecomputeengine.domain.Network;
+import org.jclouds.googlecomputeengine.domain.Operation;
+import org.jclouds.googlecomputeengine.domain.SlashEncodedIds;
+import org.jclouds.googlecomputeengine.domain.internal.NetworkAndAddressRange;
+import org.jclouds.googlecomputeengine.options.FirewallOptions;
+import org.jclouds.googlecomputeengine.options.ListOptions;
+import org.jclouds.googlecomputeengine.options.ListOptions.Builder;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+
+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.cache.LoadingCache;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+
+/**
+ * An extension to compute service to allow for the manipulation of {@link org.jclouds.compute.domain.SecurityGroup}s. Implementation
+ * is optional by providers.
+ *
+ * @author Andrew Bayer
+ */
+public class GoogleComputeEngineSecurityGroupExtension implements SecurityGroupExtension {
+
+   protected final Supplier<String> userProject;
+   protected final GroupNamingConvention.Factory namingConvention;
+   protected final LoadingCache<NetworkAndAddressRange, Network> networkCreator;
+   protected final Function<Network, SecurityGroup> groupConverter;
+   protected final GoogleComputeEngineApi api;
+   protected final Predicate<AtomicReference<Operation>> operationDonePredicate;
+   protected final long operationCompleteCheckInterval;
+   protected final long operationCompleteCheckTimeout;
+
+   @Inject
+   public GoogleComputeEngineSecurityGroupExtension(GoogleComputeEngineApi api,
+                                                    @UserProject Supplier<String> userProject,
+                                                    GroupNamingConvention.Factory namingConvention,
+                                                    LoadingCache<NetworkAndAddressRange, Network> networkCreator,
+                                                    Function<Network, SecurityGroup> groupConverter,
+                                                    @Named("global") Predicate<AtomicReference<Operation>> operationDonePredicate,
+                                                    @Named(OPERATION_COMPLETE_INTERVAL) Long operationCompleteCheckInterval,
+                                                    @Named(OPERATION_COMPLETE_TIMEOUT) Long operationCompleteCheckTimeout) {
+      this.api = checkNotNull(api, "api");
+      this.userProject = checkNotNull(userProject, "userProject");
+      this.namingConvention = checkNotNull(namingConvention, "namingConvention");
+      this.networkCreator = checkNotNull(networkCreator, "networkCreator");
+      this.groupConverter = checkNotNull(groupConverter, "groupConverter");
+      this.operationCompleteCheckInterval = checkNotNull(operationCompleteCheckInterval,
+              "operation completed check interval");
+      this.operationCompleteCheckTimeout = checkNotNull(operationCompleteCheckTimeout,
+              "operation completed check timeout");
+      this.operationDonePredicate = operationDonePredicate;
+   }
+
+   @Override
+   public Set<SecurityGroup> listSecurityGroups() {
+      return api.getNetworkApiForProject(userProject.get()).list().concat().transform(groupConverter).toSet();
+   }
+
+   @Override
+   public Set<SecurityGroup> listSecurityGroupsInLocation(final Location location) {
+      return listSecurityGroups();
+   }
+
+   @Override
+   public Set<SecurityGroup> listSecurityGroupsForNode(String id) {
+      SlashEncodedIds slashEncodedIds = SlashEncodedIds.fromSlashEncoded(id);
+
+      Instance instance = api.getInstanceApiForProject(userProject.get()).getInZone(slashEncodedIds.getFirstId(),
+              slashEncodedIds.getSecondId());
+
+      if (instance == null) {
+         return ImmutableSet.of();
+      }
+
+      ImmutableSet.Builder builder = ImmutableSet.builder();
+
+
+      for (NetworkInterface nwInterface : instance.getNetworkInterfaces()) {
+         String networkUrl = nwInterface.getNetwork().getPath();
+         Network nw = api.getNetworkApiForProject(userProject.get()).get(networkUrl.substring(networkUrl.lastIndexOf('/') + 1));
+
+         SecurityGroup grp = groupForTagsInNetwork(nw, instance.getTags().getItems());
+         if (grp != null) {
+            builder.add(grp);
+         }
+      }
+
+      return builder.build();
+   }
+
+   @Override
+   public SecurityGroup getSecurityGroupById(String id) {
+      checkNotNull(id, "id");
+      Network network = api.getNetworkApiForProject(userProject.get()).get(id);
+
+      if (network == null) {
+         return null;
+      }
+
+      return groupConverter.apply(network);
+   }
+
+   @Override
+   public SecurityGroup createSecurityGroup(String name, Location location) {
+      return createSecurityGroup(name);
+   }
+
+   public SecurityGroup createSecurityGroup(String name) {
+      checkNotNull(name, "name");
+
+      NetworkAndAddressRange nAr = new NetworkAndAddressRange(name, DEFAULT_INTERNAL_NETWORK_RANGE, null);
+
+      Network nw = networkCreator.apply(nAr);
+
+      return groupConverter.apply(nw);
+   }
+
+   @Override
+   public boolean removeSecurityGroup(String id) {
+      checkNotNull(id, "id");
+      if (api.getNetworkApiForProject(userProject.get()).get(id) == null) {
+         return false;
+      }
+
+      ListOptions options = new ListOptions.Builder().filter("network eq .*/" + id);
+
+      FluentIterable<Firewall> fws = api.getFirewallApiForProject(userProject.get()).list(options).concat();
+
+      for (Firewall fw : fws) {
+         AtomicReference<Operation> operation = new AtomicReference<Operation>(api.getFirewallApiForProject(userProject.get())
+                 .delete(fw.getName()));
+
+         retry(operationDonePredicate, operationCompleteCheckTimeout, operationCompleteCheckInterval,
+                 MILLISECONDS).apply(operation);
+
+         checkState(!operation.get().getHttpError().isPresent(), "Could not delete firewall, operation failed" + operation);
+      }
+
+      AtomicReference<Operation> operation = new AtomicReference<Operation>(
+              api.getNetworkApiForProject(userProject.get()).delete(id));
+
+      retry(operationDonePredicate, operationCompleteCheckTimeout, operationCompleteCheckInterval,
+                 MILLISECONDS).apply(operation);
+
+      checkState(!operation.get().getHttpError().isPresent(), "Could not create network, operation failed" + operation);
+
+      return true;
+   }
+
+   @Override
+   public SecurityGroup addIpPermission(IpPermission ipPermission, SecurityGroup group) {
+
+      if (api.getNetworkApiForProject(userProject.get()).get(group.getId()) == null) {
+         // Network corresponding to security group does not exist.
+         return null;
+      }
+
+      ListOptions options = new ListOptions.Builder().filter("network eq .*/" + group.getName());
+
+      if (api.getFirewallApiForProject(userProject.get()).list(options).concat().anyMatch(providesIpPermission(ipPermission))) {
+         // Permission already exists.
+         return group;
+      }
+
+      FirewallOptions fwOptions = new FirewallOptions();
+      String uniqueFwName = namingConvention.createWithoutPrefix().uniqueNameForGroup(group.getName());
+      fwOptions.name(uniqueFwName);
+      fwOptions.network(group.getUri());
+      if (ipPermission.getGroupIds().size() > 0) {
+         fwOptions.sourceTags(ipPermission.getGroupIds());
+      }
+      if (ipPermission.getCidrBlocks().size() > 0) {
+         fwOptions.sourceRanges(ipPermission.getCidrBlocks());
+      }
+      Firewall.Rule.Builder ruleBuilder = Firewall.Rule.builder();
+      ruleBuilder.IpProtocol(ipPermission.getIpProtocol());
+      if (ipPermission.getToPort() > 0) {
+         ruleBuilder.addPortRange(ipPermission.getFromPort(), ipPermission.getToPort());
+      }
+      fwOptions.addAllowedRule(ruleBuilder.build());
+
+      AtomicReference<Operation> operation = new AtomicReference<Operation>(api.getFirewallApiForProject(userProject
+              .get()).createInNetwork(
+              uniqueFwName,
+              group.getUri(),
+              fwOptions));
+
+      retry(operationDonePredicate, operationCompleteCheckTimeout, operationCompleteCheckInterval,
+              MILLISECONDS).apply(operation);
+
+      checkState(!operation.get().getHttpError().isPresent(), "Could not create firewall, operation failed" + operation);
+
+      return getSecurityGroupById(group.getId());
+   }
+
+   @Override
+   public SecurityGroup addIpPermission(IpProtocol protocol, int startPort, int endPort,
+           Multimap<String,String> tenantIdGroupNamePairs, Iterable<String> ipRanges,
+           Iterable<String> groupIds, SecurityGroup group) {
+
+      IpPermission.Builder permBuilder = IpPermission.builder();
+      permBuilder.ipProtocol(protocol);
+      permBuilder.fromPort(startPort);
+      permBuilder.toPort(endPort);
+      permBuilder.groupIds(groupIds);
+      permBuilder.cidrBlocks(ipRanges);
+
+      return addIpPermission(permBuilder.build(), group);
+
+   }
+
+   @Override
+   public SecurityGroup removeIpPermission(IpPermission ipPermission, SecurityGroup group) {
+      if (api.getNetworkApiForProject(userProject.get()).get(group.getId()) == null) {
+         // Network corresponding to security group does not exist.
+         return null;
+      }
+
+      ListOptions options = new ListOptions.Builder().filter("network eq .*/" + group.getName());
+
+      FluentIterable<Firewall> fws = api.getFirewallApiForProject(userProject.get()).list(options).concat();
+
+      for (Firewall fw : fws) {
+         if (equalsIpPermission(ipPermission).apply(fw)) {
+            AtomicReference<Operation> operation = new AtomicReference<Operation>(api.getFirewallApiForProject(userProject.get())
+                    .delete(fw.getName()));
+
+            retry(operationDonePredicate, operationCompleteCheckTimeout, operationCompleteCheckInterval,
+                    MILLISECONDS).apply(operation);
+
+            checkState(!operation.get().getHttpError().isPresent(), "Could not delete firewall, operation failed" + operation);
+         }
+      }
+
+      return getSecurityGroupById(group.getId());
+   }
+
+   @Override
+   public SecurityGroup removeIpPermission(IpProtocol protocol, int startPort, int endPort,
+                                        Multimap<String,String> tenantIdGroupNamePairs, Iterable<String> ipRanges,
+                                        Iterable<String> groupIds, SecurityGroup group) {
+
+      IpPermission.Builder permBuilder = IpPermission.builder();
+      permBuilder.ipProtocol(protocol);
+      permBuilder.fromPort(startPort);
+      permBuilder.toPort(endPort);
+      permBuilder.groupIds(groupIds);
+      permBuilder.cidrBlocks(ipRanges);
+
+      return removeIpPermission(permBuilder.build(), group);
+
+   }
+
+   @Override
+   public boolean supportsTenantIdGroupNamePairs() {
+      return false;
+   }
+
+   @Override
+   public boolean supportsTenantIdGroupIdPairs() {
+      return false;
+   }
+
+   @Override
+   public boolean supportsGroupIds() {
+      return true;
+   }
+
+   @Override
+   public boolean supportsPortRangesForGroups() {
+      return true;
+   }
+
+   private SecurityGroup groupForTagsInNetwork(Network nw, final Set <String> tags) {
+      ListOptions opts = new Builder().filter("network eq .*/" + nw.getName());
+      Set<Firewall> fws = api.getFirewallApiForProject(userProject.get()).list(opts).concat()
+              .filter(new Predicate<Firewall>() {
+                 @Override
+                 public boolean apply(final Firewall input) {
+                    // If any of the targetTags on the firewall apply or the firewall has no target tags...
+                    return Iterables.any(input.getTargetTags(), Predicates.in(tags))
+                            || Predicates.equalTo(0).apply(input.getTargetTags().size());
+                 }
+              }).toSet();
+
+      if (fws.size() > 0) {
+         return groupConverter.apply(nw);
+      }
+
+      return null;
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/FirewallToIpPermission.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/FirewallToIpPermission.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/FirewallToIpPermission.java
new file mode 100644
index 0000000..6ee86f9
--- /dev/null
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/FirewallToIpPermission.java
@@ -0,0 +1,89 @@
+/*
+ * 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 javax.annotation.Resource;
+import javax.inject.Named;
+
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.googlecomputeengine.domain.Firewall;
+import org.jclouds.googlecomputeengine.domain.Firewall.Rule;
+import org.jclouds.logging.Logger;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Range;
+
+/**
+ * A function for transforming a GCE-specific Firewall into a generic
+ * IpPermission object.
+ *
+ * @author Andrew Bayer
+ */
+public class FirewallToIpPermission implements Function<Firewall, Iterable<IpPermission>> {
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   public FirewallToIpPermission() {
+   }
+
+
+   @Override
+   public Iterable<IpPermission> apply(Firewall fw) {
+      ImmutableSet.Builder setBuilder = ImmutableSet.builder();
+
+      for (Rule rule: fw.getAllowed()) {
+         if (!rule.getPorts().isEmpty()) {
+            for (Range<Integer> r : rule.getPorts().asRanges()) {
+               IpPermission.Builder builder = populateBuilder(fw, rule.getIpProtocol());
+               builder.fromPort(r.lowerEndpoint());
+               builder.toPort(r.upperEndpoint());
+               setBuilder.add(builder.build());
+            }
+         } else {
+            setBuilder.add(populateBuilder(fw, rule.getIpProtocol()).build());
+         }
+      }
+
+      return setBuilder.build();
+   }
+
+   /**
+    * Convenience method for populating common parts of the IpPermission.
+    * @param fw
+    * @param protocol
+    * @return a pre-populated builder.
+    */
+   private IpPermission.Builder populateBuilder(Firewall fw, IpProtocol protocol) {
+      IpPermission.Builder builder = IpPermission.builder();
+
+      builder.ipProtocol(protocol);
+
+      if (!fw.getSourceRanges().isEmpty()) {
+         builder.cidrBlocks(fw.getSourceRanges());
+      }
+      if (!fw.getSourceTags().isEmpty()) {
+         builder.groupIds(fw.getSourceTags());
+      }
+
+      return builder;
+   }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/NetworkToSecurityGroup.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/NetworkToSecurityGroup.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/NetworkToSecurityGroup.java
new file mode 100644
index 0000000..ad8a95c
--- /dev/null
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/NetworkToSecurityGroup.java
@@ -0,0 +1,84 @@
+/*
+ * 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 javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.domain.SecurityGroupBuilder;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
+import org.jclouds.googlecomputeengine.config.UserProject;
+import org.jclouds.googlecomputeengine.domain.Firewall;
+import org.jclouds.googlecomputeengine.domain.Network;
+import org.jclouds.googlecomputeengine.options.ListOptions;
+import org.jclouds.logging.Logger;
+import org.jclouds.net.domain.IpPermission;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * A function for transforming a GCE-specific Network into a generic
+ * SecurityGroup object.
+ *
+ * @author Andrew Bayer
+ */
+public class NetworkToSecurityGroup implements Function<Network, SecurityGroup> {
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final Function<Firewall, Iterable<IpPermission>> firewallToPerms;
+   private final GoogleComputeEngineApi api;
+   private final Supplier<String> project;
+
+   @Inject
+   public NetworkToSecurityGroup(Function<Firewall, Iterable<IpPermission>> firewallToPerms,
+                                 GoogleComputeEngineApi api,
+                                 @UserProject Supplier<String> project) {
+      this.firewallToPerms = firewallToPerms;
+      this.api = api;
+      this.project = project;
+   }
+
+   @Override
+   public SecurityGroup apply(Network network)  {
+      SecurityGroupBuilder builder = new SecurityGroupBuilder();
+
+      builder.id(network.getName());
+      builder.providerId(network.getId());
+      builder.name(network.getName());
+      builder.uri(network.getSelfLink());
+
+      ImmutableSet.Builder permBuilder = ImmutableSet.builder();
+
+      ListOptions options = new ListOptions.Builder().filter("network eq .*/" + network.getName());
+
+      for (Firewall fw : api.getFirewallApiForProject(project.get()).list(options).concat()) {
+         permBuilder.addAll(firewallToPerms.apply(fw));
+      }
+
+      builder.ipPermissions(permBuilder.build());
+
+      return builder.build();
+   }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/loaders/FindNetworkOrCreate.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/loaders/FindNetworkOrCreate.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/loaders/FindNetworkOrCreate.java
new file mode 100644
index 0000000..120448f
--- /dev/null
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/loaders/FindNetworkOrCreate.java
@@ -0,0 +1,65 @@
+/*
+ * 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.loaders;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+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.config.UserProject;
+import org.jclouds.googlecomputeengine.domain.Network;
+import org.jclouds.googlecomputeengine.domain.internal.NetworkAndAddressRange;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.cache.CacheLoader;
+
+/**
+ * @author Andrew Bayer
+ */
+public class FindNetworkOrCreate extends CacheLoader<NetworkAndAddressRange, Network> {
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+   protected final GoogleComputeEngineApi api;
+   protected final Function<NetworkAndAddressRange, Network> networkCreator;
+   protected final Supplier<String> userProject;
+
+   @Inject
+   public FindNetworkOrCreate(GoogleComputeEngineApi api,
+                              Function<NetworkAndAddressRange, Network> networkCreator,
+                              @UserProject Supplier<String> userProject) {
+      this.api = checkNotNull(api, "api");
+      this.networkCreator = checkNotNull(networkCreator, "networkCreator");
+      this.userProject = checkNotNull(userProject, "userProject");
+   }
+
+   @Override
+   public Network load(NetworkAndAddressRange in) {
+      Network network = api.getNetworkApiForProject(userProject.get()).get(in.getName());
+      if (network != null) {
+         return network;
+      } else {
+         return networkCreator.apply(in);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/strategy/CreateNodesWithGroupEncodedIntoNameThenAddToSet.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/strategy/CreateNodesWithGroupEncodedIntoNameThenAddToSet.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/strategy/CreateNodesWithGroupEncodedIntoNameThenAddToSet.java
index 5380313..74059f9 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/strategy/CreateNodesWithGroupEncodedIntoNameThenAddToSet.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/strategy/CreateNodesWithGroupEncodedIntoNameThenAddToSet.java
@@ -45,10 +45,13 @@ import org.jclouds.googlecomputeengine.config.UserProject;
 import org.jclouds.googlecomputeengine.domain.Firewall;
 import org.jclouds.googlecomputeengine.domain.Network;
 import org.jclouds.googlecomputeengine.domain.Operation;
+import org.jclouds.googlecomputeengine.domain.internal.NetworkAndAddressRange;
 import org.jclouds.googlecomputeengine.options.FirewallOptions;
+import org.jclouds.net.domain.IpProtocol;
 
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
+import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Multimap;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -65,6 +68,7 @@ public class CreateNodesWithGroupEncodedIntoNameThenAddToSet extends
 
    private final GoogleComputeEngineApi api;
    private final Supplier<String> userProject;
+   private final LoadingCache<NetworkAndAddressRange, Network> networkMap;
    private final Predicate<AtomicReference<Operation>> operationDonePredicate;
    private final long operationCompleteCheckInterval;
    private final long operationCompleteCheckTimeout;
@@ -82,7 +86,8 @@ public class CreateNodesWithGroupEncodedIntoNameThenAddToSet extends
            @UserProject Supplier<String> userProject,
            @Named("global") Predicate<AtomicReference<Operation>> operationDonePredicate,
            @Named(OPERATION_COMPLETE_INTERVAL) Long operationCompleteCheckInterval,
-           @Named(OPERATION_COMPLETE_TIMEOUT) Long operationCompleteCheckTimeout) {
+           @Named(OPERATION_COMPLETE_TIMEOUT) Long operationCompleteCheckTimeout,
+           LoadingCache<NetworkAndAddressRange, Network> networkMap) {
       super(addNodeWithGroupStrategy, listNodesStrategy, namingConvention, userExecutor,
               customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
 
@@ -93,6 +98,7 @@ public class CreateNodesWithGroupEncodedIntoNameThenAddToSet extends
       this.operationCompleteCheckTimeout = checkNotNull(operationCompleteCheckTimeout,
               "operation completed check timeout");
       this.operationDonePredicate = operationDonePredicate;
+      this.networkMap = checkNotNull(networkMap, "networkMap");
    }
 
    @Override
@@ -123,31 +129,13 @@ public class CreateNodesWithGroupEncodedIntoNameThenAddToSet extends
 
       String networkName = templateOptions.getNetworkName().or(sharedResourceName);
 
-      // check if the network was previously created (cache???)
-      Network network = api.getNetworkApiForProject(userProject.get()).get(networkName);
-
-      if (network != null) {
-         return network;
-      } else if (templateOptions.getNetwork().isPresent()) {
-         throw new IllegalArgumentException("requested network " + networkName + " does not exist");
-      }
-
-      AtomicReference<Operation> operation = new AtomicReference<Operation>(api.getNetworkApiForProject(userProject
-              .get()).createInIPv4Range(sharedResourceName, DEFAULT_INTERNAL_NETWORK_RANGE));
-      retry(operationDonePredicate, operationCompleteCheckTimeout, operationCompleteCheckInterval,
-              MILLISECONDS).apply(operation);
-
-      checkState(!operation.get().getHttpError().isPresent(), "Could not create network, operation failed" + operation);
-
-      return checkNotNull(api.getNetworkApiForProject(userProject.get()).get(sharedResourceName),
-              "no network with name %s was found", sharedResourceName);
-
+      return networkMap.apply(new NetworkAndAddressRange(networkName, DEFAULT_INTERNAL_NETWORK_RANGE, null));
    }
 
    /**
     * Tries to find if a firewall already exists for this group, if not it creates one.
     *
-    * @see org.jclouds.googlecomputeengine.features.FirewallAsyncApi#patch(String, org.jclouds.googlecomputeengine.options.FirewallOptions)
+    * @see org.jclouds.googlecomputeengine.features.FirewallApi#patch(String, org.jclouds.googlecomputeengine.options.FirewallOptions)
     */
    private void getOrCreateFirewall(GoogleComputeEngineTemplateOptions templateOptions, Network network,
                                     String sharedResourceName) {
@@ -161,9 +149,9 @@ public class CreateNodesWithGroupEncodedIntoNameThenAddToSet extends
       ImmutableSet.Builder<Firewall.Rule> rules = ImmutableSet.builder();
 
       Firewall.Rule.Builder tcpRule = Firewall.Rule.builder();
-      tcpRule.IPProtocol(Firewall.Rule.IPProtocol.TCP);
+      tcpRule.IpProtocol(IpProtocol.TCP);
       Firewall.Rule.Builder udpRule = Firewall.Rule.builder();
-      udpRule.IPProtocol(Firewall.Rule.IPProtocol.UDP);
+      udpRule.IpProtocol(IpProtocol.UDP);
       for (Integer port : templateOptions.getInboundPorts()) {
          tcpRule.addPort(port);
          udpRule.addPort(port);

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/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 1b3fbac..ef9d7d6 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
@@ -36,6 +36,7 @@ import org.jclouds.googlecomputeengine.domain.Project;
 import org.jclouds.googlecomputeengine.options.FirewallOptions;
 import org.jclouds.googlecomputeengine.options.RouteOptions;
 import org.jclouds.json.config.GsonModule;
+import org.jclouds.net.domain.IpProtocol;
 import org.jclouds.oauth.v2.domain.ClaimSet;
 import org.jclouds.oauth.v2.domain.Header;
 import org.jclouds.oauth.v2.json.ClaimSetTypeAdapter;
@@ -380,7 +381,7 @@ public class GoogleComputeEngineParserModule extends AbstractModule {
               JsonParseException {
          JsonObject rule = json.getAsJsonObject();
          Rule.Builder builder = Rule.builder();
-         builder.IPProtocol(Rule.IPProtocol.fromValue(rule.get("IPProtocol").getAsString()));
+         builder.IpProtocol(IpProtocol.fromValue(rule.get("IPProtocol").getAsString()));
          if (rule.get("ports") != null) {
             JsonArray ports = (JsonArray) rule.get("ports");
             for (JsonElement port : ports) {
@@ -399,7 +400,7 @@ public class GoogleComputeEngineParserModule extends AbstractModule {
       @Override
       public JsonElement serialize(Firewall.Rule src, Type typeOfSrc, JsonSerializationContext context) {
          JsonObject ruleObject = new JsonObject();
-         ruleObject.addProperty("IPProtocol", src.getIPProtocol().value());
+         ruleObject.addProperty("IPProtocol", src.getIpProtocol().value());
          if (src.getPorts() != null && !src.getPorts().isEmpty()) {
             JsonArray ports = new JsonArray();
             for (Range<Integer> range : src.getPorts().asRanges()) {

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Firewall.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Firewall.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Firewall.java
index 52d51ab..78874bc 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Firewall.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Firewall.java
@@ -28,6 +28,8 @@ import java.net.URI;
 import java.util.Date;
 import java.util.Set;
 
+import org.jclouds.net.domain.IpProtocol;
+
 import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableSet;
@@ -245,32 +247,14 @@ public final class Firewall extends Resource {
     */
    public static final class Rule {
 
-      public enum IPProtocol {
-
-         TCP, UDP, ICMP, UNKNOWN;
-
-         public String value() {
-            return name().toLowerCase();
-         }
-
-         @Override
-         public String toString() {
-            return value();
-         }
-
-         public static IPProtocol fromValue(String protocol) {
-            return valueOf(protocol.toUpperCase());
-         }
-      }
-
-      private final IPProtocol ipProtocol;
+      private final IpProtocol ipProtocol;
       private final RangeSet<Integer> ports;
 
       @ConstructorProperties({
-              "IPProtocol", "ports"
+              "IpProtocol", "ports"
       })
-      private Rule(IPProtocol IPProtocol, RangeSet<Integer> ports) {
-         this.ipProtocol = checkNotNull(IPProtocol);
+      private Rule(IpProtocol IpProtocol, RangeSet<Integer> ports) {
+         this.ipProtocol = checkNotNull(IpProtocol);
          this.ports = ports == null ? TreeRangeSet.<Integer>create() : ports;
       }
 
@@ -279,7 +263,7 @@ public final class Firewall extends Resource {
        *
        * @return this is the IP protocol that is allowed for this rule.
        */
-      public IPProtocol getIPProtocol() {
+      public IpProtocol getIpProtocol() {
          return ipProtocol;
       }
 
@@ -320,7 +304,7 @@ public final class Firewall extends Resource {
        */
       public Objects.ToStringHelper string() {
          return toStringHelper(this)
-                 .add("IPProtocol", ipProtocol).add("ports", ports);
+                 .add("IpProtocol", ipProtocol).add("ports", ports);
       }
 
       /**
@@ -341,14 +325,14 @@ public final class Firewall extends Resource {
 
       public static final class Builder {
 
-         private IPProtocol ipProtocol;
+         private IpProtocol ipProtocol;
          private RangeSet<Integer> ports = TreeRangeSet.create();
 
          /**
-          * @see org.jclouds.googlecomputeengine.domain.Firewall.Rule#getIPProtocol()
+          * @see org.jclouds.googlecomputeengine.domain.Firewall.Rule#getIpProtocol()
           */
-         public Builder IPProtocol(IPProtocol IPProtocol) {
-            this.ipProtocol = IPProtocol;
+         public Builder IpProtocol(IpProtocol IpProtocol) {
+            this.ipProtocol = IpProtocol;
             return this;
          }
 
@@ -384,7 +368,7 @@ public final class Firewall extends Resource {
          }
 
          public Builder fromFirewallRule(Rule firewallRule) {
-            return new Builder().IPProtocol(firewallRule.getIPProtocol()).ports(firewallRule.getPorts());
+            return new Builder().IpProtocol(firewallRule.getIpProtocol()).ports(firewallRule.getPorts());
          }
       }
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Project.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Project.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Project.java
index 0292c71..24a139a 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Project.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Project.java
@@ -178,7 +178,7 @@ public class Project extends Resource {
               "metric", "usage", "limit"
       })
       protected Quota(String metric, Double usage, Double limit) {
-         this.metric = checkNotNull(metric, "metric");
+         this.metric = metric != null ? metric : "undefined";
          this.usage = checkNotNull(usage, "usage");
          this.limit = checkNotNull(limit, "limit");
       }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/internal/NetworkAndAddressRange.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/internal/NetworkAndAddressRange.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/internal/NetworkAndAddressRange.java
new file mode 100644
index 0000000..66fbd66
--- /dev/null
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/internal/NetworkAndAddressRange.java
@@ -0,0 +1,91 @@
+/*
+ * 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.domain.internal;
+
+import static com.google.common.base.Objects.equal;
+import static com.google.common.base.Objects.toStringHelper;
+import static com.google.common.base.Optional.fromNullable;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
+
+/**
+ * Container for network, IPv4 range and optional gateway, for creation caching
+ */
+public class NetworkAndAddressRange {
+   protected final String name;
+   protected final String ipV4Range;
+   protected final Optional<String> gateway;
+
+   @ConstructorProperties({
+           "name", "ipV4Range", "gateway"
+   })
+   public NetworkAndAddressRange(String name, String ipV4Range, @Nullable String gateway) {
+      this.name = checkNotNull(name, "name");
+      this.ipV4Range = checkNotNull(ipV4Range, "ipV4Range");
+      this.gateway = fromNullable(gateway);
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public String getIpV4Range() {
+      return ipV4Range;
+   }
+
+   @Nullable
+   public Optional<String> getGateway() {
+      return gateway;
+   }
+
+   @Override
+   public int hashCode() {
+      // We only do hashcode/equals on name.
+      // the ip v4 range and gateway are included for creation rather than caching.
+      return Objects.hashCode(name);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null || getClass() != obj.getClass()) return false;
+      NetworkAndAddressRange that = NetworkAndAddressRange.class.cast(obj);
+      return equal(this.name, that.name);
+   }
+
+   protected ToStringHelper string() {
+      return toStringHelper(this)
+              .omitNullValues()
+              .add("name", name)
+              .add("ipV4Range", ipV4Range)
+              .add("gateway", gateway.orNull());
+   }
+
+   @Override
+   public String toString() {
+      return string().toString();
+   }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/functions/CreateNetworkIfNeeded.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/functions/CreateNetworkIfNeeded.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/functions/CreateNetworkIfNeeded.java
new file mode 100644
index 0000000..09a4eee
--- /dev/null
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/functions/CreateNetworkIfNeeded.java
@@ -0,0 +1,101 @@
+/*
+ * 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.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.OPERATION_COMPLETE_INTERVAL;
+import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.OPERATION_COMPLETE_TIMEOUT;
+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 javax.inject.Singleton;
+
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
+import org.jclouds.googlecomputeengine.config.UserProject;
+import org.jclouds.googlecomputeengine.domain.Network;
+import org.jclouds.googlecomputeengine.domain.Operation;
+import org.jclouds.googlecomputeengine.domain.internal.NetworkAndAddressRange;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+
+/**
+ * @author Andrew Bayer
+ */
+@Singleton
+public class CreateNetworkIfNeeded implements Function<NetworkAndAddressRange, Network> {
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   protected final GoogleComputeEngineApi api;
+   protected final Supplier<String> userProject;
+   private final Predicate<AtomicReference<Operation>> operationDonePredicate;
+   private final long operationCompleteCheckInterval;
+   private final long operationCompleteCheckTimeout;
+
+   @Inject
+   public CreateNetworkIfNeeded(GoogleComputeEngineApi api,
+                                @UserProject Supplier<String> userProject,
+                                @Named("global") Predicate<AtomicReference<Operation>> operationDonePredicate,
+                                @Named(OPERATION_COMPLETE_INTERVAL) Long operationCompleteCheckInterval,
+                                @Named(OPERATION_COMPLETE_TIMEOUT) Long operationCompleteCheckTimeout) {
+      this.api = checkNotNull(api, "api");
+      this.userProject = checkNotNull(userProject, "userProject");
+      this.operationCompleteCheckInterval = checkNotNull(operationCompleteCheckInterval,
+              "operation completed check interval");
+      this.operationCompleteCheckTimeout = checkNotNull(operationCompleteCheckTimeout,
+              "operation completed check timeout");
+      this.operationDonePredicate = operationDonePredicate;
+   }
+
+   @Override
+   public Network apply(NetworkAndAddressRange input) {
+      checkNotNull(input, "input");
+
+      try {
+         if (input.getGateway().isPresent()) {
+            AtomicReference<Operation> operation = new AtomicReference<Operation>(api.getNetworkApiForProject(userProject
+                    .get()).createInIPv4RangeWithGateway(input.getName(), input.getIpV4Range(), input.getGateway().get()));
+            retry(operationDonePredicate, operationCompleteCheckTimeout, operationCompleteCheckInterval,
+                    MILLISECONDS).apply(operation);
+
+            checkState(!operation.get().getHttpError().isPresent(), "Could not create network, operation failed" + operation);
+         } else {
+            AtomicReference<Operation> operation = new AtomicReference<Operation>(api.getNetworkApiForProject(userProject
+                    .get()).createInIPv4Range(input.getName(), input.getIpV4Range()));
+            retry(operationDonePredicate, operationCompleteCheckTimeout, operationCompleteCheckInterval,
+                    MILLISECONDS).apply(operation);
+
+            checkState(!operation.get().getHttpError().isPresent(), "Could not create network, operation failed" + operation);
+         }
+         return checkNotNull(api.getNetworkApiForProject(userProject.get()).get(input.getName()),
+                 "no network with name %s was found", input.getName());
+      } catch (IllegalStateException e) {
+         return api.getNetworkApiForProject(userProject.get()).get(input.getName());
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/GlobalOperationDonePredicate.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/GlobalOperationDonePredicate.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/GlobalOperationDonePredicate.java
index abd2b4c..df96e4e 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/GlobalOperationDonePredicate.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/GlobalOperationDonePredicate.java
@@ -39,7 +39,7 @@ public class GlobalOperationDonePredicate implements Predicate<AtomicReference<O
    private final Supplier<String> project;
 
    @Inject
-   GlobalOperationDonePredicate(GoogleComputeEngineApi api, @UserProject Supplier<String> project) {
+   public GlobalOperationDonePredicate(GoogleComputeEngineApi api, @UserProject Supplier<String> project) {
       this.api = api;
       this.project = project;
    }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/NetworkFirewallPredicates.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/NetworkFirewallPredicates.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/NetworkFirewallPredicates.java
new file mode 100644
index 0000000..e41536e
--- /dev/null
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/predicates/NetworkFirewallPredicates.java
@@ -0,0 +1,123 @@
+/*
+ * 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.predicates;
+
+import static com.google.common.collect.Collections2.transform;
+
+import org.jclouds.googlecomputeengine.domain.Firewall;
+import org.jclouds.googlecomputeengine.domain.Firewall.Rule;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Range;
+import com.google.common.collect.Sets;
+
+public class NetworkFirewallPredicates {
+
+   public static Predicate<Firewall> hasProtocol(final IpProtocol protocol) {
+      return new Predicate<Firewall>() {
+
+         @Override
+         public boolean apply(Firewall fw) {
+            return Predicates.in(transform(fw.getAllowed(), new Function<Rule, IpProtocol>() {
+               @Override
+               public IpProtocol apply(Rule input) {
+                  return input.getIpProtocol();
+               }
+            })).apply(protocol);
+
+         }
+      };
+   }
+
+   public static Predicate<Firewall> hasPortRange(final Range<Integer> portRange) {
+      return new Predicate<Firewall>() {
+
+         @Override
+         public boolean apply(Firewall fw) {
+            return Iterables.any(fw.getAllowed(), new Predicate<Rule>() {
+               @Override
+               public boolean apply(Rule input) {
+                  return input.getPorts().encloses(portRange);
+               }
+            });
+         }
+      };
+   }
+
+   public static Predicate<Firewall> hasSourceTag(final String sourceTag) {
+      return new Predicate<Firewall>() {
+         @Override
+         public boolean apply(Firewall input) {
+            return input.getSourceTags() != null && input.getSourceTags().contains(sourceTag);
+         }
+      };
+   }
+
+   public static Predicate<Firewall> hasSourceRange(final String sourceRange) {
+      return new Predicate<Firewall>() {
+         @Override
+         public boolean apply(Firewall input) {
+            return input.getSourceRanges() != null && input.getSourceRanges().contains(sourceRange);
+         }
+      };
+   }
+
+   public static Predicate<Firewall> equalsIpPermission(final IpPermission permission) {
+      return new Predicate<Firewall>() {
+         @Override
+         public boolean apply(Firewall input) {
+            return Iterables.elementsEqual(permission.getGroupIds(), input.getSourceTags())
+                      && Iterables.elementsEqual(permission.getCidrBlocks(), input.getSourceRanges())
+                      && (input.getAllowed().size() == 1
+                             && ruleEqualsIpPermission(permission).apply(Iterables.getOnlyElement(input.getAllowed())));
+         }
+      };
+   }
+
+   public static Predicate<Firewall> providesIpPermission(final IpPermission permission) {
+      return new Predicate<Firewall>() {
+         @Override
+         public boolean apply(Firewall input) {
+            return ((permission.getGroupIds().size() == 0 && input.getSourceTags().size() == 0)
+                       || Sets.intersection(permission.getGroupIds(), input.getSourceTags()).size() > 0)
+                      && ((permission.getCidrBlocks().size() == 0 && input.getSourceRanges().size() == 0)
+                             || Sets.intersection(permission.getCidrBlocks(), input.getSourceRanges()).size() > 0)
+                      && hasProtocol(permission.getIpProtocol()).apply(input)
+                      && ((permission.getFromPort() == 0 && permission.getToPort() == 0)
+                             || hasPortRange(Range.closed(permission.getFromPort(), permission.getToPort())).apply(input));
+         }
+      };
+   }
+
+   private static Predicate<Firewall.Rule> ruleEqualsIpPermission(final IpPermission permission) {
+      return new Predicate<Rule>() {
+         @Override
+         public boolean apply(Firewall.Rule input) {
+            return permission.getIpProtocol().equals(input.getIpProtocol())
+                      && ((input.getPorts().isEmpty() && permission.getFromPort() == 0 && permission.getToPort() == 0)
+                             || (input.getPorts().asRanges().size() == 1
+                                    && permission.getFromPort() == Iterables.getOnlyElement(input.getPorts().asRanges()).lowerEndpoint()
+                                    && permission.getToPort() == Iterables.getOnlyElement(input.getPorts().asRanges()).upperEndpoint()));
+         }
+      };
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/extensions/GoogleComputeEngineSecurityGroupExtensionLiveTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/extensions/GoogleComputeEngineSecurityGroupExtensionLiveTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/extensions/GoogleComputeEngineSecurityGroupExtensionLiveTest.java
new file mode 100644
index 0000000..6f0b4c6
--- /dev/null
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/extensions/GoogleComputeEngineSecurityGroupExtensionLiveTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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.extensions;
+
+import org.jclouds.compute.extensions.internal.BaseSecurityGroupExtensionLiveTest;
+import org.testng.annotations.Test;
+
+/**
+ * @author Andrew Bayer
+ */
+@Test(groups = "live", singleThreaded = true, testName = "GoogleComputeEngineSecurityGroupExtensionLiveTest")
+public class GoogleComputeEngineSecurityGroupExtensionLiveTest extends BaseSecurityGroupExtensionLiveTest {
+
+   public GoogleComputeEngineSecurityGroupExtensionLiveTest() {
+      provider = "google-compute-engine";
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/functions/FirewallToIpPermissionTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/functions/FirewallToIpPermissionTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/functions/FirewallToIpPermissionTest.java
new file mode 100644
index 0000000..4970357
--- /dev/null
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/functions/FirewallToIpPermissionTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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 org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.Date;
+
+import org.jclouds.googlecomputeengine.domain.Firewall;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+
+public class FirewallToIpPermissionTest {
+
+   @Test
+   public void testApply() {
+
+      Firewall fw = fwForTest();
+
+      FirewallToIpPermission converter = new FirewallToIpPermission();
+
+      Iterable<IpPermission> perms = converter.apply(fw);
+
+      assertEquals(Iterables.size(perms), 3, "There should be three IpPermissions but there is only " + Iterables.size(perms));
+
+      assertTrue(Iterables.any(perms, Predicates.and(hasProtocol(IpProtocol.TCP),
+              hasStartAndEndPort(1, 10))), "No permission found for TCP, ports 1-10");
+      assertTrue(Iterables.any(perms, Predicates.and(hasProtocol(IpProtocol.TCP),
+              hasStartAndEndPort(33, 33))), "No permission found for TCP, port 33");
+      assertTrue(Iterables.any(perms, hasProtocol(IpProtocol.ICMP)),
+              "No permission found for ICMP");
+   }
+
+   public static Firewall fwForTest() {
+      Firewall.Builder builder = Firewall.builder();
+
+      builder.addSourceRange("0.0.0.0/0");
+      builder.addAllowed(Firewall.Rule.builder().IpProtocol(IpProtocol.TCP)
+              .addPortRange(1, 10).build());
+      builder.addAllowed(Firewall.Rule.builder().IpProtocol(IpProtocol.TCP)
+              .addPort(33).build());
+      builder.addAllowed(Firewall.Rule.builder().IpProtocol(IpProtocol.ICMP).build());
+      builder.id("abcd");
+      builder.selfLink(URI.create("https://www.googleapis.com/compute/v1beta15/projects/myproject/global/firewalls/jclouds-test"));
+      builder.network(URI.create("https://www.googleapis.com/compute/v1beta15/projects/myproject/global/networks/jclouds-test"));
+      builder.creationTimestamp(new Date());
+      builder.name("jclouds-test");
+
+      return builder.build();
+   }
+
+   public static Predicate<IpPermission> hasProtocol(final IpProtocol protocol) {
+      return new Predicate<IpPermission>() {
+
+         @Override
+         public boolean apply(IpPermission perm) {
+            return protocol.equals(perm.getIpProtocol());
+         }
+      };
+   }
+
+   public static Predicate<IpPermission> hasStartAndEndPort(final int startPort, final int endPort) {
+      return new Predicate<IpPermission>() {
+
+         @Override
+         public boolean apply(IpPermission perm) {
+            return startPort == perm.getFromPort() && endPort == perm.getToPort();
+         }
+      };
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/functions/NetworkToSecurityGroupTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/functions/NetworkToSecurityGroupTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/functions/NetworkToSecurityGroupTest.java
new file mode 100644
index 0000000..79a9da0
--- /dev/null
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/functions/NetworkToSecurityGroupTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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 org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.jclouds.googlecomputeengine.compute.functions.FirewallToIpPermissionTest.hasProtocol;
+import static org.jclouds.googlecomputeengine.compute.functions.FirewallToIpPermissionTest.hasStartAndEndPort;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.net.URI;
+import java.util.Date;
+
+import org.jclouds.collect.IterableWithMarkers;
+import org.jclouds.collect.PagedIterables;
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
+import org.jclouds.googlecomputeengine.domain.Network;
+import org.jclouds.googlecomputeengine.features.FirewallApi;
+import org.jclouds.googlecomputeengine.options.ListOptions;
+import org.jclouds.googlecomputeengine.options.ListOptions.Builder;
+import org.jclouds.net.domain.IpProtocol;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+public class NetworkToSecurityGroupTest {
+
+   @Test
+   public void testApply() {
+      Supplier<String> projectSupplier = new Supplier<String>() {
+         @Override
+         public String get() {
+            return "myproject";
+         }
+      };
+
+      FirewallToIpPermission fwToPerm = new FirewallToIpPermission();
+
+      GoogleComputeEngineApi api = createMock(GoogleComputeEngineApi.class);
+      FirewallApi fwApi = createMock(FirewallApi.class);
+
+      ListOptions options = new Builder().filter("network eq .*/jclouds-test");
+      expect(api.getFirewallApiForProject(projectSupplier.get()))
+              .andReturn(fwApi);
+      expect(fwApi.list(options)).andReturn(PagedIterables.of(IterableWithMarkers.from(ImmutableSet.of(FirewallToIpPermissionTest.fwForTest()))));
+
+      replay(api, fwApi);
+      Network.Builder builder = Network.builder();
+
+      builder.id("abcd");
+      builder.selfLink(URI.create("https://www.googleapis.com/compute/v1beta15/projects/myproject/global/networks/jclouds-test"));
+      builder.creationTimestamp(new Date());
+      builder.description("some description");
+      builder.gatewayIPv4("1.2.3.4");
+      builder.IPv4Range("0.0.0.0/0");
+      builder.name("jclouds-test");
+
+      Network network = builder.build();
+
+      NetworkToSecurityGroup netToSg = new NetworkToSecurityGroup(fwToPerm, api, projectSupplier);
+
+      SecurityGroup group = netToSg.apply(network);
+
+      assertEquals(group.getId(), "jclouds-test");
+      assertEquals(group.getUri(), URI.create("https://www.googleapis.com/compute/v1beta15/projects/myproject/global/networks/jclouds-test"));
+      assertEquals(group.getIpPermissions().size(), 3);
+      assertTrue(Iterables.any(group.getIpPermissions(), Predicates.and(hasProtocol(IpProtocol.TCP),
+              hasStartAndEndPort(1, 10))), "No permission found for TCP, ports 1-10");
+      assertTrue(Iterables.any(group.getIpPermissions(), Predicates.and(hasProtocol(IpProtocol.TCP),
+              hasStartAndEndPort(33, 33))), "No permission found for TCP, port 33");
+      assertTrue(Iterables.any(group.getIpPermissions(), hasProtocol(IpProtocol.ICMP)),
+              "No permission found for ICMP");
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/loaders/FindNetworkOrCreateTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/loaders/FindNetworkOrCreateTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/loaders/FindNetworkOrCreateTest.java
new file mode 100644
index 0000000..af384a5
--- /dev/null
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/loaders/FindNetworkOrCreateTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.loaders;
+
+import static com.google.common.base.Optional.fromNullable;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
+import org.jclouds.googlecomputeengine.domain.Network;
+import org.jclouds.googlecomputeengine.domain.Operation;
+import org.jclouds.googlecomputeengine.domain.internal.NetworkAndAddressRange;
+import org.jclouds.googlecomputeengine.features.GlobalOperationApi;
+import org.jclouds.googlecomputeengine.features.NetworkApi;
+import org.jclouds.googlecomputeengine.functions.CreateNetworkIfNeeded;
+import org.jclouds.googlecomputeengine.predicates.GlobalOperationDonePredicate;
+import org.jclouds.http.HttpResponse;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.LoadingCache;
+
+/**
+ * @author Andrew Bayer
+ */
+public class FindNetworkOrCreateTest {
+
+   @Test
+   public void testLoadExisting() {
+      final GoogleComputeEngineApi api = createMock(GoogleComputeEngineApi.class);
+      final NetworkApi nwApi = createMock(NetworkApi.class);
+
+      Network network = Network.builder().IPv4Range("0.0.0.0/0")
+              .id("abcd").name("this-network")
+              .selfLink(URI.create("https://www.googleapis.com/compute/v1beta15/projects/myproject/global/networks/this-network"))
+              .build();
+
+      final Supplier<String> userProject = new Supplier<String>() {
+         @Override
+         public String get() {
+            return "myproject";
+         }
+      };
+
+      expect(api.getNetworkApiForProject(userProject.get())).andReturn(nwApi).atLeastOnce();
+
+      expect(nwApi.get("this-network")).andReturn(network);
+
+      replay(api, nwApi);
+
+      NetworkAndAddressRange input = new NetworkAndAddressRange("this-network", "0.0.0.0/0", null);
+
+      GlobalOperationDonePredicate pred = new GlobalOperationDonePredicate(api, userProject);
+
+      CreateNetworkIfNeeded creator = new CreateNetworkIfNeeded(api, userProject, pred, 100l, 100l);
+
+      FindNetworkOrCreate loader = new FindNetworkOrCreate(api, creator, userProject);
+
+      LoadingCache<NetworkAndAddressRange, Network> cache = CacheBuilder.newBuilder().build(loader);
+
+      assertEquals(cache.getUnchecked(input), network);
+
+      // Second call is to ensure we only need to make the API calls once.
+      assertEquals(cache.getUnchecked(input), network);
+
+      verify(api, nwApi);
+   }
+
+   @Test
+   public void testLoadNew() {
+      final GoogleComputeEngineApi api = createMock(GoogleComputeEngineApi.class);
+      final NetworkApi nwApi = createMock(NetworkApi.class);
+      final GlobalOperationApi globalApi = createMock(GlobalOperationApi.class);
+
+      Network network = Network.builder().IPv4Range("0.0.0.0/0")
+              .id("abcd").name("this-network")
+              .selfLink(URI.create("https://www.googleapis.com/compute/v1beta15/projects/myproject/global/networks/this-network"))
+              .build();
+
+      Operation createOp = createMock(Operation.class);
+
+      final Supplier<String> userProject = new Supplier<String>() {
+         @Override
+         public String get() {
+            return "myproject";
+         }
+      };
+
+      expect(api.getNetworkApiForProject(userProject.get())).andReturn(nwApi).atLeastOnce();
+      expect(api.getGlobalOperationApiForProject(userProject.get())).andReturn(globalApi).atLeastOnce();
+
+      expect(nwApi.createInIPv4Range("this-network", "0.0.0.0/0"))
+              .andReturn(createOp);
+      expect(globalApi.get("create-op")).andReturn(createOp);
+      // pre-creation
+      expect(nwApi.get("this-network")).andReturn(null);
+      // post-creation
+      expect(nwApi.get("this-network")).andReturn(network);
+
+      expect(createOp.getName()).andReturn("create-op");
+      expect(createOp.getStatus()).andReturn(Operation.Status.DONE);
+      expect(createOp.getHttpError()).andReturn(fromNullable((HttpResponse)null));
+      replay(api, nwApi, createOp, globalApi);
+
+      NetworkAndAddressRange input = new NetworkAndAddressRange("this-network", "0.0.0.0/0", null);
+
+      GlobalOperationDonePredicate pred = new GlobalOperationDonePredicate(api, userProject);
+
+      CreateNetworkIfNeeded creator = new CreateNetworkIfNeeded(api, userProject, pred, 100l, 100l);
+
+      FindNetworkOrCreate loader = new FindNetworkOrCreate(api, creator, userProject);
+
+      LoadingCache<NetworkAndAddressRange, Network> cache = CacheBuilder.newBuilder().build(loader);
+
+      assertEquals(cache.getUnchecked(input), network);
+
+      // Second call is to ensure we only need to make the API calls once.
+      assertEquals(cache.getUnchecked(input), network);
+
+      verify(api, nwApi, globalApi, createOp);
+
+   }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-google/blob/97c911ca/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/FirewallApiExpectTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/FirewallApiExpectTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/FirewallApiExpectTest.java
index 6345c51..6a20d28 100644
--- a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/FirewallApiExpectTest.java
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/FirewallApiExpectTest.java
@@ -21,7 +21,6 @@ import static com.google.common.collect.Iterables.transform;
 import static java.lang.String.format;
 import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_READONLY_SCOPE;
 import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_SCOPE;
-import static org.jclouds.googlecomputeengine.domain.Firewall.Rule.IPProtocol;
 import static org.jclouds.io.Payloads.newStringPayload;
 import static org.jclouds.util.Strings2.toStringAndClose;
 import static org.testng.Assert.assertEquals;
@@ -43,6 +42,7 @@ import org.jclouds.googlecomputeengine.parse.ParseOperationTest;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.jclouds.io.Payload;
+import org.jclouds.net.domain.IpProtocol;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Function;
@@ -147,7 +147,7 @@ public class FirewallApiExpectTest extends BaseGoogleComputeEngineApiExpectTest
               ".com/compute/v1beta15/projects/myproject/global/networks/default"),
               new FirewallOptions()
                       .addAllowedRule(Firewall.Rule.builder()
-                              .IPProtocol(IPProtocol.TCP)
+                              .IpProtocol(IpProtocol.TCP)
                               .addPort(22)
                               .addPortRange(23, 24).build())
                       .addSourceTag("tag1")
@@ -185,7 +185,7 @@ public class FirewallApiExpectTest extends BaseGoogleComputeEngineApiExpectTest
                       .network(URI.create("https://www.googleapis" +
                               ".com/compute/v1beta15/projects/myproject/global/networks/default"))
                       .addAllowedRule(Firewall.Rule.builder()
-                              .IPProtocol(IPProtocol.TCP)
+                              .IpProtocol(IpProtocol.TCP)
                               .addPort(22)
                               .addPortRange(23, 24).build())
                       .addSourceTag("tag1")
@@ -222,7 +222,7 @@ public class FirewallApiExpectTest extends BaseGoogleComputeEngineApiExpectTest
                       .network(URI.create("https://www.googleapis" +
                               ".com/compute/v1beta15/projects/myproject/global/networks/default"))
                       .addAllowedRule(Firewall.Rule.builder()
-                              .IPProtocol(IPProtocol.TCP)
+                              .IpProtocol(IpProtocol.TCP)
                               .addPort(22)
                               .addPortRange(23, 24).build())
                       .addSourceTag("tag1")