/* * 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.location.byon; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.net.InetAddress; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import javax.annotation.Nullable; import org.apache.brooklyn.api.location.MachineLocation; import org.apache.brooklyn.api.location.MachineProvisioningLocation; import org.apache.brooklyn.api.location.NoMachinesAvailableException; import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.internal.BrooklynProperties; import org.apache.brooklyn.core.location.BasicLocationRegistry; import org.apache.brooklyn.core.location.LocationConfigKeys; import org.apache.brooklyn.core.location.NamedLocationResolver; import org.apache.brooklyn.core.location.internal.LocationInternal; import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext; import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; import org.apache.brooklyn.location.ssh.SshMachineLocation; import org.apache.brooklyn.test.Asserts; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.net.Networking; import org.apache.brooklyn.util.net.UserAndHostAndPort; import org.apache.brooklyn.util.os.Os; import org.apache.brooklyn.util.text.StringPredicates; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; public class ByonLocationResolverTest { private static final Logger log = LoggerFactory.getLogger(ByonLocationResolverTest.class); private BrooklynProperties brooklynProperties; private LocalManagementContext managementContext; private Predicate<CharSequence> defaultNamePredicate; @BeforeMethod(alwaysRun=true) public void setUp() throws Exception { managementContext = LocalManagementContextForTests.newInstance(); brooklynProperties = managementContext.getBrooklynProperties(); defaultNamePredicate = StringPredicates.startsWith(FixedListMachineProvisioningLocation.class.getSimpleName()); } @AfterMethod(alwaysRun=true) public void tearDown() throws Exception { if (managementContext != null) Entities.destroyAll(managementContext); } @Test public void testTakesByonScopedProperties() { brooklynProperties.put("brooklyn.location.byon.privateKeyFile", "myprivatekeyfile"); brooklynProperties.put("brooklyn.location.byon.publicKeyFile", "mypublickeyfile"); brooklynProperties.put("brooklyn.location.byon.privateKeyData", "myprivateKeyData"); brooklynProperties.put("brooklyn.location.byon.publicKeyData", "myPublicKeyData"); brooklynProperties.put("brooklyn.location.byon.privateKeyPassphrase", "myprivateKeyPassphrase"); Map<String, Object> conf = resolve("byon(hosts=\"1.1.1.1\")").config().getBag().getAllConfig(); assertEquals(conf.get("privateKeyFile"), "myprivatekeyfile"); assertEquals(conf.get("publicKeyFile"), "mypublickeyfile"); assertEquals(conf.get("privateKeyData"), "myprivateKeyData"); assertEquals(conf.get("publicKeyData"), "myPublicKeyData"); assertEquals(conf.get("privateKeyPassphrase"), "myprivateKeyPassphrase"); } @Test public void testNamedByonLocation() throws Exception { brooklynProperties.put("brooklyn.location.named.mynamed", "byon(hosts=\"1.1.1.1\")"); FixedListMachineProvisioningLocation<MachineLocation> loc = resolve("named:mynamed"); assertEquals(loc.obtain().getAddress(), InetAddress.getByName("1.1.1.1")); } @Test public void testPropertiesInSpec() throws Exception { FixedListMachineProvisioningLocation<MachineLocation> loc = resolve("byon(privateKeyFile=myprivatekeyfile,hosts=\"1.1.1.1\")"); SshMachineLocation machine = (SshMachineLocation)loc.obtain(); assertEquals(machine.config().getBag().getStringKey("privateKeyFile"), "myprivatekeyfile"); assertEquals(machine.getAddress(), Networking.getInetAddressWithFixedName("1.1.1.1")); } @Test public void testPropertyScopePrecedence() throws Exception { brooklynProperties.put("brooklyn.location.named.mynamed", "byon(hosts=\"1.1.1.1\")"); // prefer those in "named" over everything else brooklynProperties.put("brooklyn.location.named.mynamed.privateKeyFile", "privateKeyFile-inNamed"); brooklynProperties.put("brooklyn.location.byon.privateKeyFile", "privateKeyFile-inProviderSpecific"); brooklynProperties.put("brooklyn.localhost.privateKeyFile", "privateKeyFile-inGeneric"); // prefer those in provider-specific over generic brooklynProperties.put("brooklyn.location.byon.publicKeyFile", "publicKeyFile-inProviderSpecific"); brooklynProperties.put("brooklyn.location.publicKeyFile", "publicKeyFile-inGeneric"); // prefer location-generic if nothing else brooklynProperties.put("brooklyn.location.privateKeyData", "privateKeyData-inGeneric"); Map<String, Object> conf = resolve("named:mynamed").config().getBag().getAllConfig(); assertEquals(conf.get("privateKeyFile"), "privateKeyFile-inNamed"); assertEquals(conf.get("publicKeyFile"), "publicKeyFile-inProviderSpecific"); assertEquals(conf.get("privateKeyData"), "privateKeyData-inGeneric"); } @Test public void testThrowsOnInvalid() throws Exception { assertThrowsNoSuchElement("wrongprefix:(hosts=\"1.1.1.1\")"); assertThrowsIllegalArgument("byon"); // no hosts assertThrowsIllegalArgument("byon()"); // no hosts assertThrowsIllegalArgument("byon(hosts=\"\")"); // empty hosts assertThrowsIllegalArgument("byon(hosts=\"1.1.1.1\""); // no closing bracket assertThrowsIllegalArgument("byon(hosts=\"1.1.1.1\", name)"); // no value for name assertThrowsIllegalArgument("byon(hosts=\"1.1.1.1\", name=)"); // no value for name } @Test(expectedExceptions={IllegalArgumentException.class}) public void testRegistryCommaResolutionInListNotAllowed() throws NoMachinesAvailableException { // disallowed since 0.7.0 // fails because it interprets the entire string as a single byon spec, which does not parse managementContext.getLocationRegistry().resolve(ImmutableList.of("byon(hosts=\"192.168.0.1\",user=bob),byon(hosts=\"192.168.0.2\",user=bob2)")); } @Test public void testResolvesHosts() throws Exception { assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1\")"), ImmutableSet.of("1.1.1.1")); assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1\")"), ImmutableSet.of("1.1.1.1")); assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1,1.1.1.2\")"), ImmutableSet.of("1.1.1.1","1.1.1.2")); assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1, 1.1.1.2\")"), ImmutableSet.of("1.1.1.1","1.1.1.2")); } @Test public void testWithOldStyleColon() throws Exception { assertByonClusterEquals(resolve("byon:(hosts=\"1.1.1.1\")"), ImmutableSet.of("1.1.1.1")); assertByonClusterEquals(resolve("byon:(hosts=\"1.1.1.1\", name=myname)"), ImmutableSet.of("1.1.1.1"), "myname"); } @Test public void testUsesDisplayName() throws Exception { assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1\", name=myname)"), ImmutableSet.of("1.1.1.1"), "myname"); assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.1\", name=\"myname\")"), ImmutableSet.of("1.1.1.1"), "myname"); } @Test public void testResolvesHostsGlobExpansion() throws Exception { assertByonClusterEquals(resolve("byon(hosts=\"1.1.1.{1,2}\")"), ImmutableSet.of("1.1.1.1","1.1.1.2")); assertByonClusterEquals(resolve("byon(hosts=\"1.1.{1.1,2.{1,2}}\")"), ImmutableSet.of("1.1.1.1","1.1.2.1","1.1.2.2")); assertByonClusterEquals(resolve("byon(hosts=\"1.1.{1,2}.{1,2}\")"), ImmutableSet.of("1.1.1.1","1.1.1.2","1.1.2.1","1.1.2.2")); } @Test(groups="Integration") public void testNiceError() throws Exception { Asserts.assertFailsWith(new Runnable() { @Override public void run() { FixedListMachineProvisioningLocation<MachineLocation> x = resolve("byon(hosts=\"1.1.1.{1,2}}\")"); log.error("got "+x+" but should have failed (your DNS is giving an IP for hostname '1.1.1.1}' (with the extra '}')"); } }, new Predicate<Throwable>() { @Override public boolean apply(@Nullable Throwable input) { String s = input.toString(); // words if (!s.contains("Invalid host")) return false; // problematic entry if (!s.contains("1.1.1.1}")) return false; // original spec if (!s.contains("1.1.1.{1,2}}")) return false; return true; } }); } @Test public void testResolvesUsernameAtHost() throws Exception { assertByonClusterWithUsersEquals(resolve("byon(hosts=\"myuser@1.1.1.1\")"), ImmutableSet.of(UserAndHostAndPort.fromParts("myuser", "1.1.1.1", 22))); assertByonClusterWithUsersEquals(resolve("byon(hosts=\"myuser@1.1.1.1,myuser2@1.1.1.1\")"), ImmutableSet.of( UserAndHostAndPort.fromParts("myuser", "1.1.1.1", 22), UserAndHostAndPort.fromParts("myuser2", "1.1.1.1", 22))); assertByonClusterWithUsersEquals(resolve("byon(hosts=\"myuser@1.1.1.1,myuser2@1.1.1.2\")"), ImmutableSet.of( UserAndHostAndPort.fromParts("myuser", "1.1.1.1", 22), UserAndHostAndPort.fromParts("myuser2", "1.1.1.2", 22))); } @Test public void testResolvesUserArg() throws Exception { assertByonClusterWithUsersEquals(resolve("byon(hosts=\"1.1.1.1\",user=bob)"), ImmutableSet.of(UserAndHostAndPort.fromParts("bob", "1.1.1.1", 22))); assertByonClusterWithUsersEquals(resolve("byon(user=\"bob\",hosts=\"myuser@1.1.1.1,1.1.1.1\")"), ImmutableSet.of(UserAndHostAndPort.fromParts("myuser", "1.1.1.1", 22), UserAndHostAndPort.fromParts("bob", "1.1.1.1", 22))); } @Test public void testResolvesUserArg2() throws Exception { String spec = "byon(hosts=\"1.1.1.1\",user=bob)"; FixedListMachineProvisioningLocation<MachineLocation> ll = resolve(spec); SshMachineLocation l = (SshMachineLocation)ll.obtain(); Assert.assertEquals("bob", l.getUser()); } @SuppressWarnings("unchecked") @Test public void testResolvesUserArg3() throws Exception { String spec = "byon(hosts=\"1.1.1.1\")"; ((BasicLocationRegistry)managementContext.getLocationRegistry()).putProperties(MutableMap.of( "brooklyn.location.named.foo", spec, "brooklyn.location.named.foo.user", "bob")); ((BasicLocationRegistry)managementContext.getLocationRegistry()).updateDefinedLocations(); MachineProvisioningLocation<SshMachineLocation> ll = (MachineProvisioningLocation<SshMachineLocation>) new NamedLocationResolver().newLocationFromString(MutableMap.of(), "named:foo", managementContext.getLocationRegistry()); SshMachineLocation l = ll.obtain(MutableMap.of()); Assert.assertEquals("bob", l.getUser()); } @Test public void testResolvesPortArg() throws Exception { assertByonClusterWithUsersEquals(resolve("byon(user=bob,port=8022,hosts=\"1.1.1.1\")"), ImmutableSet.of(UserAndHostAndPort.fromParts("bob", "1.1.1.1", 8022))); assertByonClusterWithUsersEquals(resolve("byon(user=bob,port=8022,hosts=\"myuser@1.1.1.1,1.1.1.2:8901\")"), ImmutableSet.of(UserAndHostAndPort.fromParts("myuser", "1.1.1.1", 8022), UserAndHostAndPort.fromParts("bob", "1.1.1.2", 8901))); } @SuppressWarnings("unchecked") @Test /** private key should be inherited, so confirm that happens correctly */ public void testResolvesPrivateKeyArgInheritance() throws Exception { String spec = "byon(hosts=\"1.1.1.1\")"; ((BasicLocationRegistry)managementContext.getLocationRegistry()).putProperties(MutableMap.of( "brooklyn.location.named.foo", spec, "brooklyn.location.named.foo.user", "bob", "brooklyn.location.named.foo.privateKeyFile", "/tmp/x")); ((BasicLocationRegistry)managementContext.getLocationRegistry()).updateDefinedLocations(); MachineProvisioningLocation<SshMachineLocation> ll = (MachineProvisioningLocation<SshMachineLocation>) new NamedLocationResolver().newLocationFromString(MutableMap.of(), "named:foo", managementContext.getLocationRegistry()); Assert.assertEquals("/tmp/x", ll.getConfig(LocationConfigKeys.PRIVATE_KEY_FILE)); Assert.assertTrue(((LocationInternal)ll).config().getLocalRaw(LocationConfigKeys.PRIVATE_KEY_FILE).isPresent()); Assert.assertEquals("/tmp/x", ((LocationInternal)ll).config().getLocalBag().getStringKey(LocationConfigKeys.PRIVATE_KEY_FILE.getName())); Assert.assertEquals("/tmp/x", ((LocationInternal)ll).config().getBag().get(LocationConfigKeys.PRIVATE_KEY_FILE)); SshMachineLocation l = ll.obtain(MutableMap.of()); Assert.assertEquals("/tmp/x", l.getConfig(LocationConfigKeys.PRIVATE_KEY_FILE)); Assert.assertTrue(l.config().getRaw(LocationConfigKeys.PRIVATE_KEY_FILE).isPresent()); Assert.assertTrue(l.config().getLocalRaw(LocationConfigKeys.PRIVATE_KEY_FILE).isAbsent()); Assert.assertEquals("/tmp/x", l.config().getBag().getStringKey(LocationConfigKeys.PRIVATE_KEY_FILE.getName())); Assert.assertEquals("/tmp/x", l.config().getBag().getStringKey(LocationConfigKeys.PRIVATE_KEY_FILE.getName())); Assert.assertEquals("/tmp/x", l.config().getBag().get(LocationConfigKeys.PRIVATE_KEY_FILE)); } // FIXME: move @Test to the brooklyn-software-winrm module public void testResolvesLocalTempDir() throws Exception { String localTempDir = Os.mergePaths(Os.tmp(), "testResolvesUsernameAtHost"); brooklynProperties.put("brooklyn.location.byon.localTempDir", localTempDir); FixedListMachineProvisioningLocation<MachineLocation> byon = resolve("byon(hosts=\"1.1.1.1\",osFamily=\"windows\")"); MachineLocation machine = byon.obtain(); assertEquals(machine.getConfig(SshMachineLocation.LOCAL_TEMP_DIR), localTempDir); } @Test public void testMachinesObtainedInOrder() throws Exception { List<String> ips = ImmutableList.of("1.1.1.1", "1.1.1.6", "1.1.1.3", "1.1.1.4", "1.1.1.5"); String spec = "byon(hosts=\""+Joiner.on(",").join(ips)+"\")"; MachineProvisioningLocation<MachineLocation> ll = resolve(spec); for (String expected : ips) { MachineLocation obtained = ll.obtain(ImmutableMap.of()); assertEquals(obtained.getAddress().getHostAddress(), expected); } } @Test public void testEmptySpec() throws Exception { String spec = "byon"; Map<String, ?> flags = ImmutableMap.of( "hosts", ImmutableList.of("1.1.1.1", "2.2.2.22"), "name", "foo", "user", "myuser" ); MachineProvisioningLocation<MachineLocation> provisioner = resolve(spec, flags); SshMachineLocation location1 = (SshMachineLocation)provisioner.obtain(ImmutableMap.of()); Assert.assertEquals("myuser", location1.getUser()); Assert.assertEquals("1.1.1.1", location1.getAddress().getHostAddress()); } @Test public void testNonWindowsMachines() throws Exception { String spec = "byon"; Map<String, ?> flags = ImmutableMap.of( "hosts", ImmutableList.of("1.1.1.1", "2.2.2.2"), "osFamily", "linux" ); MachineProvisioningLocation<MachineLocation> provisioner = resolve(spec, flags); MachineLocation location = provisioner.obtain(ImmutableMap.of()); assertTrue(location instanceof SshMachineLocation, "Expected location to be SshMachineLocation, found " + location); } @Test public void testAdditionalConfig() throws Exception { FixedListMachineProvisioningLocation<MachineLocation> loc = resolve("byon(mykey=myval,hosts=\"1.1.1.1\")"); MachineLocation machine = loc.obtain(ImmutableMap.of()); assertEquals(machine.getConfig(ConfigKeys.newConfigKey(String.class, "mykey")), "myval"); } private void assertByonClusterEquals(FixedListMachineProvisioningLocation<? extends MachineLocation> cluster, Set<String> expectedHosts) { assertByonClusterEquals(cluster, expectedHosts, defaultNamePredicate); } private void assertByonClusterEquals(FixedListMachineProvisioningLocation<? extends MachineLocation> cluster, Set<String> expectedHosts, String expectedName) { assertByonClusterEquals(cluster, expectedHosts, Predicates.equalTo(expectedName)); } private void assertByonClusterEquals(FixedListMachineProvisioningLocation<? extends MachineLocation> cluster, Set<String> expectedHosts, Predicate<? super String> expectedName) { Set<String> actualHosts = ImmutableSet.copyOf(Iterables.transform(cluster.getMachines(), new Function<MachineLocation, String>() { @Override public String apply(MachineLocation input) { return input.getAddress().getHostName(); }})); assertEquals(actualHosts, expectedHosts); assertTrue(expectedName.apply(cluster.getDisplayName()), "name="+cluster.getDisplayName()); } private void assertByonClusterWithUsersEquals(FixedListMachineProvisioningLocation<? extends MachineLocation> cluster, Set<UserAndHostAndPort> expectedHosts) { assertByonClusterWithUsersEquals(cluster, expectedHosts, defaultNamePredicate); } private void assertByonClusterWithUsersEquals(FixedListMachineProvisioningLocation<? extends MachineLocation> cluster, Set<UserAndHostAndPort> expectedHosts, Predicate<? super String> expectedName) { Set<UserAndHostAndPort> actualHosts = ImmutableSet.copyOf(Iterables.transform(cluster.getMachines(), new Function<MachineLocation, UserAndHostAndPort>() { @Override public UserAndHostAndPort apply(MachineLocation input) { SshMachineLocation machine = (SshMachineLocation) input; return UserAndHostAndPort.fromParts(machine.getUser(), machine.getAddress().getHostName(), machine.getPort()); }})); assertEquals(actualHosts, expectedHosts); assertTrue(expectedName.apply(cluster.getDisplayName()), "name="+cluster.getDisplayName()); } private void assertThrowsNoSuchElement(String val) { try { resolve(val); fail(); } catch (NoSuchElementException e) { // success } } private void assertThrowsIllegalArgument(String val) { try { resolve(val); fail(); } catch (IllegalArgumentException e) { // success } } @SuppressWarnings("unchecked") private FixedListMachineProvisioningLocation<MachineLocation> resolve(String val) { return (FixedListMachineProvisioningLocation<MachineLocation>) managementContext.getLocationRegistry().resolve(val); } @SuppressWarnings("unchecked") private FixedListMachineProvisioningLocation<MachineLocation> resolve(String val, Map<?, ?> locationFlags) { return (FixedListMachineProvisioningLocation<MachineLocation>) managementContext.getLocationRegistry().resolve(val, locationFlags); } }