Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master 88fbe107c -> bcd39d589


PortForwarding: delete on JcloudsLocation.release

- Adds JcloudsPortForwarderExtension.closePortForwarding
- Adds PortForwardManager.forgetPortMappings(publicIpId)
- Fix jcloudsSshMachineLocation.getSshHostAndPort()
- Replace JcloudsPortforwardingLiveTest with 
JcloudsPortForwardingStubbedLiveTest


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/02d77d5e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/02d77d5e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/02d77d5e

Branch: refs/heads/master
Commit: 02d77d5ec8dead0767f9a65a36ea41102996f87c
Parents: 88fbe10
Author: Aled Sage <[email protected]>
Authored: Fri Jan 23 16:56:30 2015 +0000
Committer: Aled Sage <[email protected]>
Committed: Tue Jan 27 12:39:41 2015 +0000

----------------------------------------------------------------------
 .../location/access/PortForwardManager.java     |   5 +
 .../access/PortForwardManagerClient.java        |   5 +
 .../location/access/PortForwardManagerImpl.java |  21 ++-
 .../brooklyn/location/access/PortMapping.java   |  11 ++
 .../location/jclouds/JcloudsLocation.java       |  72 ++++++++-
 .../JcloudsPortForwarderExtension.java          |   2 +
 .../JcloudsPortForwardingLiveTest.java          | 117 --------------
 .../JcloudsPortForwardingStubbedLiveTest.java   | 154 +++++++++++++++++++
 8 files changed, 265 insertions(+), 122 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/02d77d5e/core/src/main/java/brooklyn/location/access/PortForwardManager.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/brooklyn/location/access/PortForwardManager.java 
b/core/src/main/java/brooklyn/location/access/PortForwardManager.java
index 00bcb75..6f93a4a 100644
--- a/core/src/main/java/brooklyn/location/access/PortForwardManager.java
+++ b/core/src/main/java/brooklyn/location/access/PortForwardManager.java
@@ -128,6 +128,11 @@ public interface PortForwardManager extends Location {
      */
     public boolean forgetPortMappings(Location location);
     
+    /** 
+     * Clears the port mappings associated with the given publicIpId, 
returning true if there were any matches.
+     */
+    public boolean forgetPortMappings(String publicIpId);
+    
     public String toVerboseString();
 
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/02d77d5e/core/src/main/java/brooklyn/location/access/PortForwardManagerClient.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/brooklyn/location/access/PortForwardManagerClient.java 
b/core/src/main/java/brooklyn/location/access/PortForwardManagerClient.java
index f3767fd..7b7c091 100644
--- a/core/src/main/java/brooklyn/location/access/PortForwardManagerClient.java
+++ b/core/src/main/java/brooklyn/location/access/PortForwardManagerClient.java
@@ -134,6 +134,11 @@ public class PortForwardManagerClient implements 
PortForwardManager {
     }
 
     @Override
+    public boolean forgetPortMappings(String publicIpId) {
+        return getDelegate().forgetPortMappings(publicIpId);
+    }
+
+    @Override
     public String getId() {
         return getDelegate().getId();
     }    

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/02d77d5e/core/src/main/java/brooklyn/location/access/PortForwardManagerImpl.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/brooklyn/location/access/PortForwardManagerImpl.java 
b/core/src/main/java/brooklyn/location/access/PortForwardManagerImpl.java
index c9a596b..16b8371 100644
--- a/core/src/main/java/brooklyn/location/access/PortForwardManagerImpl.java
+++ b/core/src/main/java/brooklyn/location/access/PortForwardManagerImpl.java
@@ -199,7 +199,7 @@ public class PortForwardManagerImpl extends 
AbstractLocation implements PortForw
                 if (publicIpId.equals(m.publicIpId) && 
privatePort==m.privatePort)
                     return getPublicHostAndPort(m);
             }
-}
+        }
         return null;
     }
     
@@ -234,6 +234,25 @@ public class PortForwardManagerImpl extends 
AbstractLocation implements PortForw
     }
     
     @Override
+    public boolean forgetPortMappings(String publicIpId) {
+        List<PortMapping> result = Lists.newArrayList();
+        synchronized (mutex) {
+            for (Iterator<PortMapping> iter = mappings.values().iterator(); 
iter.hasNext();) {
+                PortMapping m = iter.next();
+                if (publicIpId.equals(m.publicIpId)) {
+                    iter.remove();
+                    result.add(m);
+                }
+            }
+        }
+        if (log.isDebugEnabled()) log.debug("cleared all port mappings for 
"+publicIpId+" - "+result);
+        if (!result.isEmpty()) {
+            onChanged();
+        }
+        return !result.isEmpty();
+    }
+    
+    @Override
     protected ToStringHelper string() {
         int size;
         synchronized (mutex) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/02d77d5e/core/src/main/java/brooklyn/location/access/PortMapping.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/access/PortMapping.java 
b/core/src/main/java/brooklyn/location/access/PortMapping.java
index 85f49ce..aaf0ca5 100644
--- a/core/src/main/java/brooklyn/location/access/PortMapping.java
+++ b/core/src/main/java/brooklyn/location/access/PortMapping.java
@@ -19,8 +19,12 @@
 package brooklyn.location.access;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Nullable;
+
 import brooklyn.location.Location;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import com.google.common.net.HostAndPort;
 
@@ -50,6 +54,13 @@ public class PortMapping {
         this.privatePort = privatePort;
     }
 
+    // In a release after 0.7.0, this will no longer be @Nullable
+    @Beta
+    @Nullable
+    public HostAndPort getPublicEndpoint() {
+        return publicEndpoint;
+    }
+
     public int getPublicPort() {
         return publicPort;
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/02d77d5e/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------
diff --git 
a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
 
b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
index 3640117..fb78a44 100644
--- 
a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
+++ 
b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
@@ -96,6 +96,7 @@ import 
brooklyn.location.MachineManagementMixins.MachineMetadata;
 import 
brooklyn.location.MachineManagementMixins.RichMachineProvisioningLocation;
 import brooklyn.location.NoMachinesAvailableException;
 import brooklyn.location.access.PortForwardManager;
+import brooklyn.location.access.PortMapping;
 import brooklyn.location.basic.BasicMachineMetadata;
 import brooklyn.location.basic.LocationConfigKeys;
 import brooklyn.location.basic.LocationConfigUtils;
@@ -1581,7 +1582,7 @@ public class JcloudsLocation extends 
AbstractCloudMachineProvisioningLocation im
             return 
getManagementContext().getLocationManager().createLocation(LocationSpec.create(JcloudsSshMachineLocation.class)
                     .configure("displayName", vmHostname)
                     .configure("address", address)
-                    .configure("port", sshHostAndPort.isPresent() ? 
sshHostAndPort.get().getPort() : node.getLoginPort())
+                    .configure(JcloudsSshMachineLocation.SSH_PORT, 
sshHostAndPort.isPresent() ? sshHostAndPort.get().getPort() : 
node.getLoginPort())
                     .configure("user", getUser(setup))
                     // don't think "config" does anything
                     .configure(sshConfig)
@@ -1663,14 +1664,27 @@ public class JcloudsLocation extends 
AbstractCloudMachineProvisioningLocation im
         
         LOG.info("Releasing machine {} in {}, instance id {}", new Object[] 
{machine, this, instanceId});
         
-        removeChild(machine);
-        
+        Exception tothrow = null;
+        try {
+            releasePortForwarding(machine);
+        } catch (Exception e) {
+            LOG.error("Problem releasing port-forwarding for machine 
"+machine+" in "+this+", instance id "+instanceId+
+                    "; discarding instance and continuing...", e);
+            tothrow = e;
+        }
+
         try {
             releaseNode(instanceId);
         } catch (Exception e) {
             LOG.error("Problem releasing machine "+machine+" in "+this+", 
instance id "+instanceId+
                     "; discarding instance and continuing...", e);
-            throw Exceptions.propagate(e);
+            tothrow = e;
+        }
+
+        removeChild(machine);
+
+        if (tothrow != null) {
+            throw Exceptions.propagate(tothrow);
         }
     }
 
@@ -1714,6 +1728,56 @@ public class JcloudsLocation extends 
AbstractCloudMachineProvisioningLocation im
         }
     }
 
+    protected void releasePortForwarding(SshMachineLocation machine) {
+        // TODO Implementation needs revisisted. It relies on deprecated 
PortForwardManager methods.
+        
+        boolean usePortForwarding = 
Boolean.TRUE.equals(machine.getConfig(USE_PORT_FORWARDING));
+        JcloudsPortForwarderExtension portForwarder = 
machine.getConfig(PORT_FORWARDER);
+        PortForwardManager portForwardManager = 
machine.getConfig(PORT_FORWARDING_MANAGER);
+        NodeMetadata node = (machine instanceof JcloudsSshMachineLocation) ? 
((JcloudsSshMachineLocation) machine).getNode() : null;
+
+        if (portForwarder == null) {
+            LOG.debug("No port-forwarding to close (because portForwarder 
null) on release of " + machine);
+        } else {
+            // Release the port-forwarding for the login-port, which was 
explicilty created by JcloudsLocation 
+            if (usePortForwarding && node != null) {
+                HostAndPort sshHostAndPortOverride = 
machine.getSshHostAndPort();
+                LOG.debug("Closing port-forwarding at {} for machine {}: 
{}->{}", new Object[] {this, machine, sshHostAndPortOverride, 
node.getLoginPort()});
+                portForwarder.closePortForwarding(node, node.getLoginPort(), 
sshHostAndPortOverride, Protocol.TCP);
+            }
+
+            // Get all the other port-forwarding mappings for this VM, and 
release all of those
+            Set<PortMapping> mappings;
+            if (portForwardManager != null) {
+                mappings = Sets.newLinkedHashSet();
+                
mappings.addAll(portForwardManager.getLocationPublicIpIds(machine));
+                if (node != null) {
+                    
mappings.addAll(portForwardManager.getPortMappingWithPublicIpId(node.getId()));
+                }
+            } else {
+                mappings = ImmutableSet.of();
+            }
+            
+            for (PortMapping mapping : mappings) {
+                HostAndPort publicEndpoint = mapping.getPublicEndpoint();
+                int targetPort = mapping.getPrivatePort();
+                Protocol protocol = Protocol.TCP;
+                if (publicEndpoint != null) {
+                    LOG.debug("Closing port-forwarding at {} for machine {}: 
{}->{}", new Object[] {this, machine, publicEndpoint, targetPort});
+                    portForwarder.closePortForwarding(node, targetPort, 
publicEndpoint, protocol);
+                }
+            }
+        }
+        
+        // Forget all port mappings associated with this VM
+        if (portForwardManager != null) {
+            portForwardManager.forgetPortMappings(machine);
+            if (node != null) {
+                portForwardManager.forgetPortMappings(node.getId());
+            }
+        }
+    }
+
     // ------------ support methods --------------------
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/02d77d5e/locations/jclouds/src/main/java/brooklyn/location/jclouds/networking/JcloudsPortForwarderExtension.java
----------------------------------------------------------------------
diff --git 
a/locations/jclouds/src/main/java/brooklyn/location/jclouds/networking/JcloudsPortForwarderExtension.java
 
b/locations/jclouds/src/main/java/brooklyn/location/jclouds/networking/JcloudsPortForwarderExtension.java
index e3921b8..adfdde3 100644
--- 
a/locations/jclouds/src/main/java/brooklyn/location/jclouds/networking/JcloudsPortForwarderExtension.java
+++ 
b/locations/jclouds/src/main/java/brooklyn/location/jclouds/networking/JcloudsPortForwarderExtension.java
@@ -40,4 +40,6 @@ public interface JcloudsPortForwarderExtension {
      * will know about the mapped port.
      */
     public HostAndPort openPortForwarding(NodeMetadata node, int targetPort, 
Optional<Integer> optionalPublicPort, Protocol protocol, Cidr accessingCidr);
+
+    public void closePortForwarding(NodeMetadata node, int targetPort, 
HostAndPort publicHostAndPort, Protocol protocol);
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/02d77d5e/locations/jclouds/src/test/java/brooklyn/location/jclouds/networking/JcloudsPortForwardingLiveTest.java
----------------------------------------------------------------------
diff --git 
a/locations/jclouds/src/test/java/brooklyn/location/jclouds/networking/JcloudsPortForwardingLiveTest.java
 
b/locations/jclouds/src/test/java/brooklyn/location/jclouds/networking/JcloudsPortForwardingLiveTest.java
deleted file mode 100644
index c9d8393..0000000
--- 
a/locations/jclouds/src/test/java/brooklyn/location/jclouds/networking/JcloudsPortForwardingLiveTest.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.location.jclouds.networking;
-
-import static org.testng.Assert.assertEquals;
-
-import java.util.List;
-import java.util.Map;
-
-import org.jclouds.compute.domain.NodeMetadata;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.location.jclouds.AbstractJcloudsLiveTest;
-import brooklyn.location.jclouds.JcloudsLocation;
-import brooklyn.location.jclouds.JcloudsSshMachineLocation;
-import brooklyn.location.jclouds.networking.JcloudsPortForwarderExtension;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.net.Cidr;
-import brooklyn.util.net.Protocol;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.net.HostAndPort;
-
-/**
- * Tests different login options for ssh keys, passwords, etc.
- */
-public class JcloudsPortForwardingLiveTest extends AbstractJcloudsLiveTest {
-
-    private static final Logger LOG = 
LoggerFactory.getLogger(JcloudsPortForwardingLiveTest.class);
-
-    public static final String AWS_EC2_REGION_NAME = 
AWS_EC2_USEAST_REGION_NAME;
-    public static final String AWS_EC2_LOCATION_SPEC = "jclouds:" + 
AWS_EC2_PROVIDER + ":" + AWS_EC2_REGION_NAME;
-    
-    // Image: {id=us-east-1/ami-7d7bfc14, providerId=ami-7d7bfc14, 
name=RightImage_CentOS_6.3_x64_v5.8.8.5, location={scope=REGION, id=us-east-1, 
description=us-east-1, parent=aws-ec2, iso3166Codes=[US-VA]}, 
os={family=centos, arch=paravirtual, version=6.0, 
description=rightscale-us-east/RightImage_CentOS_6.3_x64_v5.8.8.5.manifest.xml, 
is64Bit=true}, 
description=rightscale-us-east/RightImage_CentOS_6.3_x64_v5.8.8.5.manifest.xml, 
version=5.8.8.5, status=AVAILABLE[available], loginUser=root, 
userMetadata={owner=411009282317, rootDeviceType=instance-store, 
virtualizationType=paravirtual, hypervisor=xen}}
-    public static final String AWS_EC2_CENTOS_IMAGE_ID = 
"us-east-1/ami-7d7bfc14";
-
-    @BeforeMethod(alwaysRun=true)
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        jcloudsLocation = (JcloudsLocation) 
managementContext.getLocationRegistry().resolve(AWS_EC2_LOCATION_SPEC);
-    }
-
-    @Test(groups = {"Live"})
-    protected void testPortForwardingCallsForwarder() throws Exception {
-        final List<HostAndPort> forwards = Lists.newCopyOnWriteArrayList();
-        
-        JcloudsSshMachineLocation machine = 
obtainEc2Machine(ImmutableMap.<ConfigKey<?>,Object>of(
-                JcloudsLocation.USE_PORT_FORWARDING, true,
-                JcloudsLocation.PORT_FORWARDER, new 
JcloudsPortForwarderExtension() {
-                    @Override public HostAndPort 
openPortForwarding(NodeMetadata node, int targetPort, Optional<Integer> 
optionalPublicPort, Protocol protocol, Cidr accessingCidr) {
-                        String vmIp = 
Iterables.get(Iterables.concat(node.getPublicAddresses(), 
node.getPrivateAddresses()), 0);
-                        HostAndPort result = HostAndPort.fromParts(vmIp, 
targetPort);
-                        forwards.add(result);
-                        return result;
-                    }
-                }));
-        
-        assertEquals(forwards.size(), 1, "forwards="+forwards+"; 
machine="+machine);
-        assertEquals(machine.getAddress().getHostAddress(), 
forwards.get(0).getHostText(), "actual="+forwards+"; machine="+machine);
-        assertEquals(machine.getPort(), forwards.get(0).getPort(), 
"forwards="+forwards+"; machine="+machine);
-        assertSshable(machine);
-    }
-    
-    @Test(groups = {"Live"})
-    protected void testPortForwardingUsesGivenPort() throws Exception {
-        final List<HostAndPort> forwards = Lists.newCopyOnWriteArrayList();
-        
-        JcloudsSshMachineLocation machine = 
obtainEc2Machine(ImmutableMap.<ConfigKey<?>,Object>of(
-                JcloudsLocation.WAIT_FOR_SSHABLE, false,
-                JcloudsLocation.USE_PORT_FORWARDING, true,
-                JcloudsLocation.PORT_FORWARDER, new 
JcloudsPortForwarderExtension() {
-                    @Override public HostAndPort 
openPortForwarding(NodeMetadata node, int targetPort, Optional<Integer> 
optionalPublicPort, Protocol protocol, Cidr accessingCidr) {
-                        HostAndPort result = HostAndPort.fromParts("1.2.3.4", 
12345);
-                        forwards.add(result);
-                        return result;
-                    }
-                }));
-        
-        assertEquals(forwards.size(), 1, "forwards="+forwards+"; 
machine="+machine);
-        assertEquals(machine.getAddress().getHostAddress(), "1.2.3.4", 
"forwards="+forwards+"; machine="+machine);
-        assertEquals(machine.getPort(), 12345, "forwards="+forwards+"; 
machine="+machine);
-    }
-    
-    private JcloudsSshMachineLocation obtainEc2Machine(Map<?,?> conf) throws 
Exception {
-        return obtainMachine(MutableMap.<Object,Object>builder()
-                .putAll(conf)
-                .putIfAbsent("imageId", AWS_EC2_CENTOS_IMAGE_ID)
-                .putIfAbsent("hardwareId", AWS_EC2_SMALL_HARDWARE_ID)
-                .putIfAbsent("inboundPorts", ImmutableList.of(22))
-                .build());
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/02d77d5e/locations/jclouds/src/test/java/brooklyn/location/jclouds/networking/JcloudsPortForwardingStubbedLiveTest.java
----------------------------------------------------------------------
diff --git 
a/locations/jclouds/src/test/java/brooklyn/location/jclouds/networking/JcloudsPortForwardingStubbedLiveTest.java
 
b/locations/jclouds/src/test/java/brooklyn/location/jclouds/networking/JcloudsPortForwardingStubbedLiveTest.java
new file mode 100644
index 0000000..f562ebf
--- /dev/null
+++ 
b/locations/jclouds/src/test/java/brooklyn/location/jclouds/networking/JcloudsPortForwardingStubbedLiveTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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 brooklyn.location.jclouds.networking;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import java.util.List;
+
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadata.Status;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.domain.LoginCredentials;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.location.access.PortForwardManager;
+import brooklyn.location.access.PortForwardManagerImpl;
+import brooklyn.location.jclouds.AbstractJcloudsStubbedLiveTest;
+import brooklyn.location.jclouds.JcloudsLocation;
+import brooklyn.location.jclouds.JcloudsSshMachineLocation;
+import brooklyn.util.net.Cidr;
+import brooklyn.util.net.Protocol;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.net.HostAndPort;
+
+/**
+ * The VM creation is stubbed out, but it still requires live access (i.e. 
real account credentials)
+ * to generate the template etc.
+ * 
+ * We supply a ComputeServiceRegistry that delegates to the real instance for 
everything except
+ * VM creation and deletion. For those operations, it delegates to a 
NodeCreator that 
+ * returns a dummy NodeMetadata, recording all calls made to it.
+ */
+public class JcloudsPortForwardingStubbedLiveTest extends 
AbstractJcloudsStubbedLiveTest {
+
+    @SuppressWarnings("unused")
+    private static final Logger LOG = 
LoggerFactory.getLogger(JcloudsPortForwardingStubbedLiveTest.class);
+
+    static class RecordingJcloudsPortForwarderExtension implements 
JcloudsPortForwarderExtension {
+        int nextPublicPort = 12345;
+        final List<List<Object>> opens = Lists.newCopyOnWriteArrayList();
+        final List<List<Object>> closes = Lists.newCopyOnWriteArrayList();
+        
+        @Override public HostAndPort openPortForwarding(NodeMetadata node, int 
targetPort, Optional<Integer> optionalPublicPort, Protocol protocol, Cidr 
accessingCidr) {
+            opens.add(ImmutableList.of(node, targetPort, optionalPublicPort, 
protocol, accessingCidr));
+            return HostAndPort.fromParts("1.2.3.4", nextPublicPort++);
+        }
+        @Override public void closePortForwarding(NodeMetadata node, int 
targetPort, HostAndPort publicHostAndPort, Protocol protocol) {
+            closes.add(ImmutableList.of(node, targetPort, publicHostAndPort, 
protocol));
+        }
+    }
+
+    @Override
+    protected NodeCreator newNodeCreator() {
+        return new NodeCreator() {
+            @Override
+            protected NodeMetadata newNode(String group, Template template) {
+                NodeMetadata result = new NodeMetadataBuilder()
+                        .id("myid")
+                        
.credentials(LoginCredentials.builder().identity("myuser").credential("mypassword").build())
+                        .loginPort(22)
+                        .status(Status.RUNNING)
+                        .publicAddresses(ImmutableList.of("173.194.32.123"))
+                        .privateAddresses(ImmutableList.of("172.168.10.11"))
+                        .build();
+                return result;
+            }
+        };
+    }
+
+    @Test(groups = {"Live", "Live-sanity"})
+    protected void testPortForwardingCallsForwarder() throws Exception {
+        RecordingJcloudsPortForwarderExtension portForwarder = new 
RecordingJcloudsPortForwarderExtension();
+        
+        JcloudsSshMachineLocation machine = 
obtainMachine(ImmutableMap.<ConfigKey<?>,Object>of(
+                JcloudsLocation.USE_PORT_FORWARDING, true,
+                JcloudsLocation.PORT_FORWARDER, portForwarder));
+        
+        NodeMetadata created = nodeCreator.created.get(0);
+        assertEquals(nodeCreator.created.size(), 1, 
"created="+nodeCreator.created+"; machine="+machine);
+        assertEquals(machine.getNode(), created);
+        assertEquals(portForwarder.opens.size(), 1, 
"opens="+portForwarder.opens+"; machine="+machine);
+        assertEquals(portForwarder.opens.get(0).get(0), created);
+        assertEquals(portForwarder.opens.get(0).get(1), 22);
+        assertEquals(portForwarder.opens.get(0).get(3), Protocol.TCP);
+        assertEquals(portForwarder.opens.get(0).get(4), Cidr.UNIVERSAL);
+        assertEquals(machine.getSshHostAndPort(), 
HostAndPort.fromParts("1.2.3.4", 12345));
+        
+        releaseMachine(machine);
+        String destroyed = nodeCreator.destroyed.get(0);
+        assertEquals(nodeCreator.destroyed.size(), 1, 
"destroyed="+nodeCreator.destroyed+"; machine="+machine);
+        assertEquals(destroyed, created.getId());
+        assertEquals(portForwarder.closes.size(), 1, 
"closes="+portForwarder.closes+"; machine="+machine);
+        assertEquals(portForwarder.closes.get(0).get(0), created);
+        assertEquals(portForwarder.closes.get(0).get(1), 22);
+        assertEquals(portForwarder.closes.get(0).get(2), 
HostAndPort.fromParts("1.2.3.4", 12345));
+        assertEquals(portForwarder.closes.get(0).get(3), Protocol.TCP);
+    }
+    
+    @Test(groups = {"Live", "Live-sanity"})
+    protected void testDeregistersWithPortForwardManagerOnRelease() throws 
Exception {
+        PortForwardManager pfm = new PortForwardManagerImpl();
+        
+        RecordingJcloudsPortForwarderExtension portForwarder = new 
RecordingJcloudsPortForwarderExtension();
+        
+        JcloudsSshMachineLocation machine = 
obtainMachine(ImmutableMap.<ConfigKey<?>,Object>of(
+                JcloudsLocation.PORT_FORWARDER, portForwarder,
+                JcloudsLocation.PORT_FORWARDING_MANAGER, pfm));
+        
+        // Add an association for this machine - expect that to be deleted 
when the machine is released.
+        HostAndPort publicHostAndPort = HostAndPort.fromParts("1.2.3.4", 1234);
+        pfm.associate("mypublicip", publicHostAndPort, machine, 80);
+        assertEquals(pfm.lookup(machine, 80), publicHostAndPort);
+        assertEquals(pfm.lookup("mypublicip", 80), publicHostAndPort);
+
+        // Release
+        releaseMachine(machine);
+        
+        // Expect to have been cleared from PortForwardManager's records
+        assertNull(pfm.lookup(machine, 80));
+        assertNull(pfm.lookup("mypublicip", 80));
+        
+        // And for port-forwarding to have been closed
+        assertEquals(portForwarder.closes.size(), 1, 
"closes="+portForwarder.closes+"; machine="+machine);
+        assertEquals(portForwarder.closes.get(0).get(1), 80);
+        assertEquals(portForwarder.closes.get(0).get(2), 
HostAndPort.fromParts("1.2.3.4", 1234));
+        assertEquals(portForwarder.closes.get(0).get(3), Protocol.TCP);
+    }
+}

Reply via email to