/*
* 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.apache.brooklyn.core.location.access;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.fail;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.location.LocationSpec;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.location.SupportsPortForwarding;
import org.apache.brooklyn.core.location.access.BrooklynAccessUtils;
import org.apache.brooklyn.core.location.access.PortForwardManager;
import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
import org.apache.brooklyn.core.test.entity.TestEntity;
import org.apache.brooklyn.location.ssh.SshMachineLocation;
import org.apache.brooklyn.util.net.Cidr;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.net.HostAndPort;
public class BrooklynAccessUtilsTest extends BrooklynAppUnitTestSupport {
protected PortForwardManager pfm;
private TestEntity entity;
@BeforeMethod(alwaysRun=true)
@Override
public void setUp() throws Exception {
super.setUp();
pfm = (PortForwardManager) mgmt.getLocationRegistry().resolve("portForwardManager(scope=global)");
}
@Test
public void testBrooklynAccessibleAddressFindsPreexistingMapping() throws Exception {
final int privatePort = 8080;
final String publicNatIp = "1.2.3.4";
final int publicNatPort = 12000;
SshMachineLocation machine = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
.configure(SshMachineLocation.TCP_PORT_MAPPINGS, ImmutableMap.of(privatePort, publicNatIp+":"+publicNatPort)));
entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
.configure(BrooklynAccessUtils.PORT_FORWARDING_MANAGER, pfm)
.location(machine));
assertEquals(BrooklynAccessUtils.getBrooklynAccessibleAddress(entity, privatePort), HostAndPort.fromParts(publicNatIp, publicNatPort));
}
@Test
public void testBrooklynAccessibleAddressUsesPrivateHostPortIfNoMapping() throws Exception {
final String privateIp = "127.1.2.3";
final int privatePort = 8080;
SshMachineLocation machine = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class));
entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
.configure(BrooklynAccessUtils.PORT_FORWARDING_MANAGER, pfm)
.location(machine));
entity.sensors().set(Attributes.HOSTNAME, privateIp);
assertEquals(BrooklynAccessUtils.getBrooklynAccessibleAddress(entity, privatePort), HostAndPort.fromParts(privateIp, privatePort));
}
@Test
public void testBrooklynAccessibleAddressFailsIfNoMappingAndNoHostname() throws Exception {
final int privatePort = 8080;
SshMachineLocation machine = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class));
entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
.configure(BrooklynAccessUtils.PORT_FORWARDING_MANAGER, pfm)
.location(machine));
try {
BrooklynAccessUtils.getBrooklynAccessibleAddress(entity, privatePort);
fail();
} catch (IllegalStateException e) {
if (!e.toString().contains("no host.name")) throw e;
// success
}
}
@Test
public void testBrooklynAccessibleAddressRequestsNewPortForwarding() throws Exception {
final int privatePort = 8080;
RecordingSupportsPortForwarding machine = mgmt.getLocationManager().createLocation(LocationSpec.create(RecordingSupportsPortForwarding.class));
entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
.configure(BrooklynAccessUtils.PORT_FORWARDING_MANAGER, pfm)
.configure(BrooklynAccessUtils.MANAGEMENT_ACCESS_CIDR, Cidr.UNIVERSAL)
.location(machine));
HostAndPort result = BrooklynAccessUtils.getBrooklynAccessibleAddress(entity, privatePort);
HostAndPort portForwarded = machine.mappings.get(privatePort);
assertNotNull(portForwarded);
assertEquals(result, portForwarded);
}
@SuppressWarnings("serial") // TODO location should not be serializable; will be changed in future release
public static class RecordingSupportsPortForwarding extends SshMachineLocation implements SupportsPortForwarding {
protected final String publicIp = "1.2.3.4";
protected final Map<Integer, HostAndPort> mappings = Maps.newConcurrentMap();
private final AtomicInteger nextPort = new AtomicInteger(12000);
@Override
public HostAndPort getSocketEndpointFor(Cidr accessor, int privatePort) {
HostAndPort result = mappings.get(privatePort);
if (result == null) {
int publicPort = nextPort.getAndIncrement();
result = HostAndPort.fromParts(publicIp, publicPort);
mappings.put(privatePort, result);
}
return result;
}
}
}