| /* |
| * 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.entity.dns; |
| |
| import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEqualsContinually; |
| import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEventually; |
| import static org.testng.Assert.assertFalse; |
| import static org.testng.Assert.assertTrue; |
| |
| import java.util.Collection; |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| |
| import org.apache.brooklyn.api.entity.Entity; |
| import org.apache.brooklyn.api.entity.EntitySpec; |
| import org.apache.brooklyn.api.entity.ImplementedBy; |
| import org.apache.brooklyn.api.location.Location; |
| import org.apache.brooklyn.api.location.LocationRegistry; |
| import org.apache.brooklyn.api.location.LocationResolver; |
| import org.apache.brooklyn.api.location.LocationSpec; |
| import org.apache.brooklyn.api.mgmt.ManagementContext; |
| import org.apache.brooklyn.config.ConfigKey; |
| import org.apache.brooklyn.core.config.ConfigKeys; |
| import org.apache.brooklyn.core.entity.Attributes; |
| import org.apache.brooklyn.core.entity.Entities; |
| import org.apache.brooklyn.core.entity.EntityAsserts; |
| import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; |
| import org.apache.brooklyn.core.location.BasicLocationRegistry; |
| import org.apache.brooklyn.core.location.LocationConfigKeys; |
| import org.apache.brooklyn.core.location.Locations; |
| import org.apache.brooklyn.core.location.Machines; |
| import org.apache.brooklyn.core.location.SimulatedLocation; |
| import org.apache.brooklyn.core.location.geo.HostGeoInfo; |
| import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; |
| import org.apache.brooklyn.core.test.entity.TestEntity; |
| import org.apache.brooklyn.entity.group.DynamicFabric; |
| import org.apache.brooklyn.entity.group.DynamicGroup; |
| import org.apache.brooklyn.entity.group.DynamicRegionsFabric; |
| import org.apache.brooklyn.location.ssh.SshMachineLocation; |
| import org.apache.brooklyn.util.collections.CollectionFunctionals; |
| import org.apache.brooklyn.util.exceptions.Exceptions; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.testng.Assert; |
| import org.testng.annotations.BeforeMethod; |
| import org.testng.annotations.Test; |
| |
| import com.google.common.base.Predicates; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Iterables; |
| |
| public class AbstractGeoDnsServiceTest extends BrooklynAppUnitTestSupport { |
| |
| public static final Logger log = LoggerFactory.getLogger(AbstractGeoDnsServiceTest.class); |
| |
| private static final String WEST_IP = "100.0.0.1"; |
| private static final String EAST_IP = "100.0.0.2"; |
| private static final double WEST_LATITUDE = 0, WEST_LONGITUDE = -60; |
| private static final double EAST_LATITUDE = 0, EAST_LONGITUDE = 60; |
| |
| private static final String NORTH_IP = "10.0.0.1"; |
| private static final double NORTH_LATITUDE = 60, NORTH_LONGITUDE = 0; |
| |
| private Location westParent; |
| private Location westChild; |
| private Location westChildWithLocation; |
| private Location eastParent; |
| private Location eastChild; |
| private Location eastChildWithLocationAndWithPrivateHostname; |
| |
| private Location northParent; |
| private Location northChildWithLocation; |
| |
| private DynamicRegionsFabric fabric; |
| private DynamicGroup testEntities; |
| private GeoDnsTestService geoDns; |
| |
| @Override |
| @BeforeMethod(alwaysRun=true) |
| public void setUp() throws Exception { |
| super.setUp(); |
| |
| westParent = newSimulatedLocation("West parent", WEST_LATITUDE, WEST_LONGITUDE); |
| |
| // west uses public IP for name, so is always picked up |
| westChild = newSshMachineLocation("West child", WEST_IP, westParent); |
| westChildWithLocation = newSshMachineLocation("West child with location", WEST_IP, WEST_IP, westParent, WEST_LATITUDE, WEST_LONGITUDE); |
| |
| // east has public IP but private IP hostname, so should also be picked up but by a different path |
| eastParent = newSimulatedLocation("East parent", EAST_LATITUDE, EAST_LONGITUDE); |
| eastChild = newSshMachineLocation("East child", EAST_IP, eastParent); |
| eastChildWithLocationAndWithPrivateHostname = newSshMachineLocation("East child with location", "localhost", EAST_IP, eastParent, EAST_LATITUDE, EAST_LONGITUDE); |
| |
| // north has a private IP and private hostname so should not be picked up when we turn off ADD_ANYTHING |
| northParent = newSimulatedLocation("North parent", NORTH_LATITUDE, NORTH_LONGITUDE); |
| northChildWithLocation = newSshMachineLocation("North child", "localhost", NORTH_IP, northParent, NORTH_LATITUDE, NORTH_LONGITUDE); |
| ((BasicLocationRegistry) mgmt.getLocationRegistry()).registerResolver(new LocationResolver() { |
| @Override public boolean isEnabled() { return true; } |
| @Override |
| public LocationSpec<? extends Location> newLocationSpecFromString(String spec, Map<?, ?> locationFlags, LocationRegistry registry) { |
| if (!spec.equals("test:north")) throw new IllegalStateException("unsupported"); |
| return newSshMachineLocationSpec("North child", "localhost", NORTH_IP, northParent, NORTH_LATITUDE, NORTH_LONGITUDE); |
| } |
| @Override public void init(ManagementContext managementContext) {} |
| @Override public String getPrefix() { return "test"; } |
| @Override |
| public boolean accepts(String spec, LocationRegistry registry) { |
| return spec.startsWith(getPrefix()); |
| } |
| }); |
| |
| Locations.manage(westParent, mgmt); |
| Locations.manage(eastParent, mgmt); |
| Locations.manage(northParent, mgmt); |
| |
| fabric = app.createAndManageChild(EntitySpec.create(DynamicRegionsFabric.class) |
| .configure(DynamicFabric.MEMBER_SPEC, EntitySpec.create(TestEntity.class))); |
| |
| testEntities = app.createAndManageChild(EntitySpec.create(DynamicGroup.class) |
| .configure(DynamicGroup.ENTITY_FILTER, Predicates.instanceOf(TestEntity.class))); |
| |
| geoDns = app.createAndManageChild(EntitySpec.create(GeoDnsTestService.class) |
| .configure(AbstractGeoDnsService.ENTITY_PROVIDER, testEntities)); |
| } |
| |
| private SimulatedLocation newSimulatedLocation(String name, double lat, double lon) { |
| return mgmt.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class) |
| .displayName(name) |
| .configure("latitude", lat) |
| .configure("longitude", lon)); |
| } |
| |
| private Location newSshMachineLocation(String name, String address, Location parent) { |
| return mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class) |
| .parent(parent) |
| .displayName(name) |
| .configure("address", address)); |
| } |
| |
| private Location newSshMachineLocation(String name, String hostname, String address, Location parent, double lat, double lon) { |
| return mgmt.getLocationManager().createLocation(newSshMachineLocationSpec(name, hostname, address, parent, lat, lon)); |
| } |
| private LocationSpec<SshMachineLocation> newSshMachineLocationSpec(String name, String hostname, String address, Location parent, double lat, double lon) { |
| return LocationSpec.create(SshMachineLocation.class) |
| .parent(parent) |
| .displayName(name) |
| .configure("hostname", hostname) |
| .configure("address", address) |
| .configure("latitude", lat) |
| .configure("longitude", lon); |
| } |
| |
| @Test(groups="Integration") |
| public void testGeoInfoOnLocation() { |
| app.start( ImmutableList.of(westChildWithLocation, eastChildWithLocationAndWithPrivateHostname) ); |
| publishSensors(2, true, true, true); |
| |
| assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(2)); |
| assertIsTarget("West child with location"); |
| assertIsTarget("East child with location"); |
| } |
| |
| @Test(groups="Integration") |
| public void testGeoInfoOnParentLocation() { |
| app.start( ImmutableList.of(westChild, eastChild) ); |
| publishSensors(2, true, false, false); |
| |
| assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(2)); |
| assertIsTarget("West child"); |
| assertIsTarget("East child"); |
| } |
| |
| @Test(groups="Integration") |
| public void testSubscribesToHostname() { |
| geoDns.config().set(GeoDnsTestServiceImpl.ADD_ANYTHING, false); |
| app.start( ImmutableList.of(westChild, eastChildWithLocationAndWithPrivateHostname) ); |
| Assert.assertEquals(geoDns.getTargetHostsByName().size(), 0); |
| publishSensors(2, true, true, true); |
| |
| assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(2)); |
| Assert.assertEquals(geoDns.getTargetHostsByName().size(), 2); |
| assertIsTarget("West child"); |
| assertIsTarget("East child with location"); |
| } |
| |
| protected void publishSensors(int expectedSize, boolean includeServiceUp, boolean includeHostname, boolean includeAddress) { |
| // First wait for the right size of group; the dynamic group gets notified asynchronously |
| // of nodes added/removed, so if we don't wait then might not set value for all members. |
| EntityAsserts.assertGroupSizeEqualsEventually(testEntities, expectedSize); |
| |
| for (Entity e: testEntities.getMembers()) { |
| if (includeServiceUp) |
| e.sensors().set(Attributes.SERVICE_UP, true); |
| |
| SshMachineLocation l = Machines.findUniqueMachineLocation(e.getLocations(), SshMachineLocation.class).get(); |
| if (includeAddress) |
| e.sensors().set(Attributes.ADDRESS, l.getAddress().getHostAddress()); |
| String h = (String) l.config().getBag().getStringKey("hostname"); |
| if (h==null) h = l.getAddress().getHostName(); |
| if (includeHostname) |
| e.sensors().set(Attributes.HOSTNAME, h); |
| } |
| } |
| |
| @Test(groups="Integration") |
| public void testChildAddedLate() { |
| app.start( ImmutableList.of(westChild, eastChildWithLocationAndWithPrivateHostname) ); |
| publishSensors(2, true, false, false); |
| assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(2)); |
| |
| String id3 = fabric.addRegion("test:north"); |
| publishSensors(3, true, false, false); |
| try { |
| assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(3)); |
| } catch (Throwable e) { |
| log.warn("Did not pick up third entity, targets are "+geoDns.getAttribute(AbstractGeoDnsService.TARGETS)+" (rethrowing): "+e); |
| Exceptions.propagate(e); |
| } |
| assertIsTarget("North child"); |
| |
| log.info("targets: "+geoDns.getTargetHostsByName()); |
| } |
| |
| @Test(groups="Integration") |
| public void testFiltersEntirelyPrivate() { |
| geoDns.config().set(GeoDnsTestServiceImpl.ADD_ANYTHING, false); |
| app.start( ImmutableList.of(westChild, eastChildWithLocationAndWithPrivateHostname, northChildWithLocation) ); |
| Assert.assertEquals(geoDns.getTargetHostsByName().size(), 0); |
| publishSensors(3, true, true, true); |
| |
| assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(2)); |
| Assert.assertEquals(geoDns.getTargetHostsByName().size(), 2); |
| assertIsTarget("West child"); |
| assertIsTarget("East child with location"); |
| assertIsNotTarget("North child"); |
| } |
| |
| // TODO BROOKLYN-272, Disabled, because fails non-deterministically in jenkins: |
| // org.apache.brooklyn.util.exceptions.PropagatedRuntimeException: failed succeeds-eventually, 69 attempts, 30003ms elapsed: AssertionError: val={R48yHLqg=<address-ignored>, eBHFc4Qd=<address-ignored>} |
| // at org.apache.brooklyn.test.Asserts.fail(Asserts.java:721) |
| // at org.apache.brooklyn.test.Asserts.assertTrue(Asserts.java:703) |
| // at org.apache.brooklyn.core.entity.EntityAsserts$2.run(EntityAsserts.java:92) |
| // at org.apache.brooklyn.test.Asserts$RunnableAdapter.call(Asserts.java:1277) |
| // at org.apache.brooklyn.test.Asserts.succeedsEventually(Asserts.java:930) |
| // at org.apache.brooklyn.test.Asserts.succeedsEventually(Asserts.java:854) |
| // at org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEventually(EntityAsserts.java:89) |
| // at org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEventually(EntityAsserts.java:84) |
| // at org.apache.brooklyn.entity.dns.AbstractGeoDnsServiceTest.testFiltersForRunningEntities(AbstractGeoDnsServiceTest.java:263) |
| @Test(groups={"Broken"}) |
| public void testFiltersForRunningEntities() { |
| app.start(ImmutableList.of(westChildWithLocation, eastChildWithLocationAndWithPrivateHostname)); |
| publishSensors(2, true, true, true); |
| |
| TestEntity problemChild = Iterables.get(Entities.descendantsAndSelf(app, TestEntity.class), 0); |
| assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(2)); |
| problemChild.sensors().set(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); |
| assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(1)); |
| problemChild.sensors().set(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); |
| assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(2)); |
| } |
| |
| @Test(groups="Integration") |
| public void testCanDisableFilterForRunningEntities() throws Exception { |
| geoDns.config().set(AbstractGeoDnsService.FILTER_FOR_RUNNING, false); |
| app.start(ImmutableList.of(westChildWithLocation, eastChildWithLocationAndWithPrivateHostname)); |
| publishSensors(2, true, true, true); |
| |
| assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(2)); |
| final Map<String, String> targets = ImmutableMap.copyOf(geoDns.sensors().get(AbstractGeoDnsService.TARGETS)); |
| TestEntity problemChild = Iterables.get(Entities.descendantsAndSelf(app, TestEntity.class), 0); |
| problemChild.sensors().set(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); |
| assertAttributeEqualsContinually(geoDns, AbstractGeoDnsService.TARGETS, targets); |
| } |
| |
| private void assertIsTarget(String target) { |
| assertTrue(geoDns.getTargetHostsByName().containsKey(target), "targets=" + geoDns.getTargetHostsByName()); |
| } |
| |
| private void assertIsNotTarget(String target) { |
| assertFalse(geoDns.getTargetHostsByName().containsKey(target), "targets=" + geoDns.getTargetHostsByName()); |
| } |
| |
| @ImplementedBy(GeoDnsTestServiceImpl.class) |
| public static interface GeoDnsTestService extends AbstractGeoDnsService { |
| public Map<String, HostGeoInfo> getTargetHostsByName(); |
| } |
| |
| public static class GeoDnsTestServiceImpl extends AbstractGeoDnsServiceImpl implements GeoDnsTestService { |
| public Map<String, HostGeoInfo> targetHostsByName = new LinkedHashMap<String, HostGeoInfo>(); |
| |
| public static final ConfigKey<Boolean> ADD_ANYTHING = ConfigKeys.newBooleanConfigKey("test.add.always", "", true); |
| |
| public GeoDnsTestServiceImpl() { |
| } |
| |
| @Override |
| public Map<String, HostGeoInfo> getTargetHostsByName() { |
| synchronized (targetHostsByName) { |
| return ImmutableMap.copyOf(targetHostsByName); |
| } |
| } |
| |
| @Override |
| protected boolean addTargetHost(Entity e) { |
| if (!getConfig(ADD_ANYTHING)) { |
| return super.addTargetHost(e); |
| } else { |
| //ignore geo lookup, override parent menu |
| if (e.getLocations().isEmpty()) { |
| log.info("GeoDns TestService ignoring target host {} (no location)", e); |
| return false; |
| } |
| Location l = Iterables.getOnlyElement(e.getLocations()); |
| HostGeoInfo geoInfo = new HostGeoInfo("<address-ignored>", l.getDisplayName(), |
| l.getConfig(LocationConfigKeys.LATITUDE), l.getConfig(LocationConfigKeys.LONGITUDE)); |
| log.info("GeoDns TestService adding target host {} {}", e, geoInfo); |
| targetHosts.put(e, geoInfo); |
| return true; |
| } |
| } |
| |
| @Override |
| protected void reconfigureService(Collection<HostGeoInfo> targetHosts) { |
| synchronized (targetHostsByName) { |
| targetHostsByName.clear(); |
| for (HostGeoInfo host : targetHosts) { |
| if (host != null) targetHostsByName.put(host.displayName, host); |
| } |
| } |
| } |
| |
| @Override |
| public String getHostname() { |
| return "localhost"; |
| } |
| } |
| |
| } |