From 728fb467b52b23607aa1b35f47e48a71e7b0f1b1 Mon Sep 17 00:00:00 2001 From: Sam Corbett Date: Wed, 22 Feb 2017 16:40:27 +0000 Subject: [PATCH] wip: review comments --- .../location/cloud/CloudLocationConfig.java | 23 +- ...tomizer.java => ConnectivityResolver.java} | 21 +- .../jclouds/ConnectivityResolverOptions.java | 245 +++++++++++++++++ ....java => DefaultConnectivityResolver.java} | 256 ++++++++++-------- .../location/jclouds/JcloudsLocation.java | 130 ++++----- .../jclouds/JcloudsLocationConfig.java | 6 +- .../ManagementAddressResolveOptions.java | 158 ----------- .../AbstractJcloudsStubbedLiveTest.java | 3 +- ...sicLocationNetworkInfoInitializerTest.java | 6 +- ...a => DefaultConnectivityResolverTest.java} | 82 +++--- ...ByonLocationResolverStubbedRebindTest.java | 4 +- .../JcloudsReachableAddressStubbedTest.java | 24 +- .../brooklyn/entity/AbstractEc2LiveTest.java | 17 -- 13 files changed, 544 insertions(+), 431 deletions(-) rename locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/{LocationNetworkInfoCustomizer.java => ConnectivityResolver.java} (64%) create mode 100644 locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ConnectivityResolverOptions.java rename locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/{BasicLocationNetworkInfoCustomizer.java => DefaultConnectivityResolver.java} (64%) delete mode 100644 locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ManagementAddressResolveOptions.java rename locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/{BasicLocationNetworkInfoCustomizerTest.java => DefaultConnectivityResolverTest.java} (78%) diff --git a/core/src/main/java/org/apache/brooklyn/core/location/cloud/CloudLocationConfig.java b/core/src/main/java/org/apache/brooklyn/core/location/cloud/CloudLocationConfig.java index baa6565d669..d5051eaf633 100644 --- a/core/src/main/java/org/apache/brooklyn/core/location/cloud/CloudLocationConfig.java +++ b/core/src/main/java/org/apache/brooklyn/core/location/cloud/CloudLocationConfig.java @@ -82,16 +82,17 @@ public interface CloudLocationConfig { "vmNameSaltLength", "Number of characters to use for a random identifier inserted in hostname " + "to uniquely identify machines", 4); - public static final ConfigKey POLL_FOR_FIRST_REACHABLE_ADDRESS = ConfigKeys.newStringConfigKey("pollForFirstReachableAddress", - "Whether and how long to wait for reaching the VM's ip:port; " - + "if 'false', will default to the node's first public IP (or private if no public IPs); " - + "if 'true' uses default duration; otherwise accepts a time string e.g. '5m' (the default) or a number of milliseconds", "5m"); + ConfigKey POLL_FOR_FIRST_REACHABLE_ADDRESS = ConfigKeys.newStringConfigKey("pollForFirstReachableAddress", + "Whether and how long to wait for reaching the VM's ip:port to be accessible over SSH or WinRM; " + + "if 'false', the location will will choose a public or private IP as appropriate; " + + "if 'true' uses default duration; otherwise accepts a time string e.g. '5m' (the default) or a number of milliseconds", + "5m"); @SuppressWarnings("serial") ConfigKey> POLL_FOR_FIRST_REACHABLE_ADDRESS_PREDICATE = ConfigKeys.newConfigKey( new TypeToken>(){}, "pollForFirstReachableAddress.predicate", - "Predicate implementation which checks whether machine is up or not."); + "Predicate implementation which checks whether an ip:port is reachable."); @SuppressWarnings("serial") ConfigKey>> POLL_FOR_FIRST_REACHABLE_ADDRESS_PREDICATE_TYPE = ConfigKeys.newConfigKey( @@ -101,15 +102,19 @@ public interface CloudLocationConfig { "Other keys prefixed with pollForFirstReachableAddress.predicate. will be passed to the Map constructor of the Predicate implementation.", Networking.IsReachablePredicate.class); - public static final ConfigKey WAIT_FOR_SSHABLE = ConfigKeys.newStringConfigKey("waitForSshable", + /** @deprecated since 0.11.0 use {@link #POLL_FOR_FIRST_REACHABLE_ADDRESS} instead. */ + @Deprecated + ConfigKey WAIT_FOR_SSHABLE = ConfigKeys.newStringConfigKey("waitForSshable", "Whether and how long to wait for a newly provisioned VM to be accessible via ssh; " + "if 'false', won't check; if 'true' uses default duration; otherwise accepts a time string e.g. '5m' (the default) or a number of milliseconds", "5m"); - public static final ConfigKey WAIT_FOR_WINRM_AVAILABLE = ConfigKeys.newStringConfigKey("waitForWinRmAvailable", + /** @deprecated since 0.11.0 use {@link #POLL_FOR_FIRST_REACHABLE_ADDRESS} instead. */ + @Deprecated + ConfigKey WAIT_FOR_WINRM_AVAILABLE = ConfigKeys.newStringConfigKey("waitForWinRmAvailable", "Whether and how long to wait for a newly provisioned VM to be accessible via WinRm; " + - "if 'false', won't check; if 'true' uses default duration; otherwise accepts a time string e.g. '30m' (the default) or a number of milliseconds", "30m"); + "if 'false', won't check; if 'true' uses default duration; otherwise accepts a time string e.g. '30m' (the default) or a number of milliseconds", "30m"); - public static final ConfigKey LOG_CREDENTIALS = ConfigKeys.newBooleanConfigKey( + ConfigKey LOG_CREDENTIALS = ConfigKeys.newBooleanConfigKey( "logCredentials", "Whether to log credentials of a new VM - strongly recommended never be used in production, as it is a big security hole!", false); diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/LocationNetworkInfoCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ConnectivityResolver.java similarity index 64% rename from locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/LocationNetworkInfoCustomizer.java rename to locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ConnectivityResolver.java index 907f0eb40f5..30ae603552e 100644 --- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/LocationNetworkInfoCustomizer.java +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ConnectivityResolver.java @@ -19,28 +19,37 @@ package org.apache.brooklyn.location.jclouds; +import org.apache.brooklyn.api.sensor.AttributeSensor; +import org.apache.brooklyn.core.sensor.Sensors; import org.apache.brooklyn.util.core.config.ConfigBag; import org.jclouds.compute.domain.NodeMetadata; import com.google.common.annotations.Beta; +import com.google.common.reflect.TypeToken; /** * LocationNetworkInfoCustomizers are used by {@link JcloudsLocation} to determine the host and port * on which connections to locations should be made and the credentials that should be used. * - * @see JcloudsLocationConfig#LOCATION_NETWORK_INFO_CUSTOMIZER + * @see JcloudsLocationConfig#CONNECTIVITY_RESOLVER */ @Beta -public interface LocationNetworkInfoCustomizer extends JcloudsLocationCustomizer { +public interface ConnectivityResolver { + + AttributeSensor> PUBLIC_ADDRESSES = Sensors.newSensor(new TypeToken>() {}, + "host.addresses.public", "Public addresses on an instance"); + + AttributeSensor> PRIVATE_ADDRESSES = Sensors.newSensor(new TypeToken>() {}, + "host.addresses.private", "Private addresses on an instance"); /** - * @param location The caller - * @param node The node the caller has created - * @param config The configuration the caller used to create the node + * @param location The caller + * @param node The node the caller has created + * @param config The configuration the caller used to create the node * @param resolveOptions Additional options the caller has chosen when creating the node * @return The HostAndPort and LoginCredentials to use when connecting to the node. */ ManagementAddressResolveResult resolve( - JcloudsLocation location, NodeMetadata node, ConfigBag config, ManagementAddressResolveOptions resolveOptions); + JcloudsLocation location, NodeMetadata node, ConfigBag config, ConnectivityResolverOptions resolveOptions); } diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ConnectivityResolverOptions.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ConnectivityResolverOptions.java new file mode 100644 index 00000000000..9af1e606aea --- /dev/null +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ConnectivityResolverOptions.java @@ -0,0 +1,245 @@ +/* + * 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.jclouds; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.apache.brooklyn.util.time.Duration; +import org.jclouds.domain.LoginCredentials; + +import com.google.common.annotations.Beta; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.net.HostAndPort; + +/** + * Holds parameters to be used by a {@link ConnectivityResolver}. + */ +@Beta +public class ConnectivityResolverOptions { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private boolean isWindows = false; + + private boolean waitForConnectable; + private boolean pollForReachableAddresses; + private Predicate reachableAddressPredicate; + private Duration reachableAddressTimeout; + private boolean propagatePollForReachableFailure; + + private LoginCredentials initialCredentials; + private LoginCredentials userCredentials; + + private int defaultLoginPort; + private boolean usePortForwarding; + private HostAndPort portForwardSshOverride; + private boolean isRebinding; + private boolean skipJcloudsSshing; + + public Builder pollForReachableAddresses( + @Nonnull Predicate reachable, + @Nonnull Duration timeout, + boolean propagatePollFailure) { + this.pollForReachableAddresses = true; + this.reachableAddressPredicate = reachable; + this.reachableAddressTimeout = timeout; + this.propagatePollForReachableFailure = propagatePollFailure; + return this; + } + + public Builder noPollForReachableAddresses() { + this.pollForReachableAddresses = false; + this.reachableAddressPredicate = null; + this.reachableAddressTimeout = null; + this.propagatePollForReachableFailure = false; + return this; + } + + public Builder initialCredentials(@Nullable LoginCredentials initialCredentials) { + this.initialCredentials = initialCredentials; + return this; + } + + public Builder userCredentials(@Nullable LoginCredentials userCredentials) { + this.userCredentials = userCredentials; + return this; + } + + public Builder isWindows(boolean windows) { + isWindows = windows; + return this; + } + + /** Indicate the host and port that should be used over all others. Normally used in tandem with a port forwarder. */ + public Builder portForwardSshOverride(@Nullable HostAndPort hostAndPortOverride) { + this.portForwardSshOverride = hostAndPortOverride; + return this; + } + + public Builder isRebinding(boolean isRebinding) { + this.isRebinding = isRebinding; + return this; + } + + public Builder skipJcloudsSshing(boolean skipJcloudsSshing) { + this.skipJcloudsSshing = skipJcloudsSshing; + return this; + } + + public Builder defaultLoginPort(int defaultLoginPort) { + this.defaultLoginPort = defaultLoginPort; + return this; + } + + public Builder usePortForwarding(boolean usePortForwarding) { + this.usePortForwarding = usePortForwarding; + return this; + } + + public Builder waitForConnectable(boolean waitForConnectable) { + this.waitForConnectable = waitForConnectable; + return this; + } + + public ConnectivityResolverOptions build() { + return new ConnectivityResolverOptions( + isWindows, waitForConnectable, pollForReachableAddresses, reachableAddressPredicate, + propagatePollForReachableFailure, reachableAddressTimeout, initialCredentials, userCredentials, + defaultLoginPort, usePortForwarding, portForwardSshOverride, isRebinding, skipJcloudsSshing); + } + } + + private final boolean isWindows; + + /** Wait for Windows machines to be available over WinRM and other machines over SSH */ + // TODO: Merge this with pollForReachable when waitForSshable and waitForWinRmable deleted. + private final boolean waitForConnectable; + + /** Wait for a machine's ip:port to be available. */ + private final boolean pollForReachableAddresses; + private final Predicate reachableAddressPredicate; + private final boolean propagatePollForReachableFailure; + private final Duration reachableAddressTimeout; + + private final LoginCredentials initialCredentials; + private final LoginCredentials userCredentials; + private final int defaultLoginPort; + + // TODO: Can usePortForwarding and portForwardSshOverride be merged? + private final boolean usePortForwarding; + private final HostAndPort portForwardSshOverride; + private final boolean isRebinding; + private final boolean skipJcloudsSshing; + + + protected ConnectivityResolverOptions(boolean isWindows, boolean waitForConnectable, boolean pollForReachableAddresses, Predicate reachableAddressPredicate, boolean propagatePollForReachableFailure, Duration reachableAddressTimeout, LoginCredentials initialCredentials, LoginCredentials userCredentials, int defaultLoginPort, boolean usePortForwarding, HostAndPort portForwardSshOverride, boolean isRebinding, boolean skipJcloudsSshing) { + this.isWindows = isWindows; + this.waitForConnectable = waitForConnectable; + this.pollForReachableAddresses = pollForReachableAddresses; + this.reachableAddressPredicate = reachableAddressPredicate; + this.propagatePollForReachableFailure = propagatePollForReachableFailure; + this.reachableAddressTimeout = reachableAddressTimeout; + this.initialCredentials = initialCredentials; + this.userCredentials = userCredentials; + this.defaultLoginPort = defaultLoginPort; + this.usePortForwarding = usePortForwarding; + this.portForwardSshOverride = portForwardSshOverride; + this.isRebinding = isRebinding; + this.skipJcloudsSshing = skipJcloudsSshing; + } + + public boolean isWindows() { + return isWindows; + } + + public boolean waitForConnectable() { + return waitForConnectable; + } + + public boolean pollForReachableAddresses() { + return pollForReachableAddresses; + } + + public Predicate reachableAddressPredicate() { + return reachableAddressPredicate; + } + + public Duration reachableAddressTimeout() { + return reachableAddressTimeout; + } + + public boolean propagatePollForReachableFailure() { + return propagatePollForReachableFailure; + } + + public Optional initialCredentials() { + return Optional.fromNullable(initialCredentials); + } + + public Optional userCredentials() { + return Optional.fromNullable(userCredentials); + } + + public boolean usePortForwarding() { + return usePortForwarding; + } + + public Optional portForwardSshOverride() { + return Optional.fromNullable(portForwardSshOverride); + } + + public int defaultLoginPort() { + return defaultLoginPort; + } + + public boolean skipJcloudsSshing() { + return skipJcloudsSshing; + } + + public boolean isRebinding() { + return isRebinding; + } + + public Builder toBuilder() { + Builder builder = builder() + .isWindows(isWindows) + .waitForConnectable(waitForConnectable) + .usePortForwarding(usePortForwarding) + .portForwardSshOverride(portForwardSshOverride) + .skipJcloudsSshing(skipJcloudsSshing) + .initialCredentials(initialCredentials) + .userCredentials(userCredentials) + .isRebinding(isRebinding) + .defaultLoginPort(defaultLoginPort) + ; + if (pollForReachableAddresses) { + builder.pollForReachableAddresses(reachableAddressPredicate, reachableAddressTimeout, propagatePollForReachableFailure); + } else { + builder.noPollForReachableAddresses(); + } + return builder; + } + +} diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicLocationNetworkInfoCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/DefaultConnectivityResolver.java similarity index 64% rename from locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicLocationNetworkInfoCustomizer.java rename to locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/DefaultConnectivityResolver.java index f1a532d6e41..b7c90eb3ddd 100644 --- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicLocationNetworkInfoCustomizer.java +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/DefaultConnectivityResolver.java @@ -21,23 +21,21 @@ import java.util.Iterator; import java.util.Map; +import javax.annotation.Nullable; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.EntityInitializer; import org.apache.brooklyn.api.entity.EntityLocal; -import org.apache.brooklyn.api.sensor.AttributeSensor; 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.BrooklynConfigKeys; import org.apache.brooklyn.core.location.LocationConfigKeys; import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; -import org.apache.brooklyn.core.sensor.Sensors; -import org.apache.brooklyn.location.winrm.WinRmMachineLocation; +import org.apache.brooklyn.core.objs.BasicConfigurableObject; import org.apache.brooklyn.util.core.config.ConfigBag; import org.apache.brooklyn.util.core.task.Tasks; import org.apache.brooklyn.util.exceptions.Exceptions; -import org.apache.brooklyn.util.guava.Maybe; import org.apache.brooklyn.util.net.Networking; import org.apache.brooklyn.util.time.Duration; import org.jclouds.compute.domain.NodeMetadata; @@ -51,14 +49,16 @@ import com.google.common.base.Predicate; import com.google.common.base.Stopwatch; import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; +import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.net.HostAndPort; /** - * BasicLocationNetworkInfoCustomizer provides the default implementation of - * {@link LocationNetworkInfoCustomizer}. It exposes options to have JcloudsLocation + * DefaultConnectivityResolver provides the default implementation of + * {@link ConnectivityResolver}. It exposes options to have JcloudsLocation * prefer to contact VMs on private addresses and can be injected on a * per-entity basis. For example: *
@@ -66,7 +66,7 @@
  * - type: server
  *   location: the-same-private-network-as-brooklyn
  *   brooklyn.initializers:
- *   - type: org.apache.brooklyn.location.jclouds.BasicLocationNetworkInfoCustomizer
+ *   - type: org.apache.brooklyn.location.jclouds.DefaultConnectivityResolver
  *     brooklyn.config:
  *       mode: ONLY_PRIVATE
  * - type: server
@@ -78,20 +78,20 @@
  * fallback is possible by replacing ONLY_PRIVATE with PREFER_PRIVATE. There are PUBLIC variants of
  * each of these.
  * 

- * BasicLocationNetworkInfoCustomizer is the default location network info customizer used by - * {@link JcloudsLocation} when {@link JcloudsLocationConfig#LOCATION_NETWORK_INFO_CUSTOMIZER} + * DefaultConnectivityResolver is the default location network info customizer used by + * {@link JcloudsLocation} when {@link JcloudsLocationConfig#CONNECTIVITY_RESOLVER} * is unset. *

* When used as an {@link EntityInitializer} the instance inserts itself into the entity's - * provisioning properties under the {@link JcloudsLocationConfig#LOCATION_NETWORK_INFO_CUSTOMIZER} + * provisioning properties under the {@link JcloudsLocationConfig#CONNECTIVITY_RESOLVER} * subkey. *

* This class is annotated @Beta and is likely to change in the future. */ @Beta -public class BasicLocationNetworkInfoCustomizer extends BasicJcloudsLocationCustomizer implements LocationNetworkInfoCustomizer { +public class DefaultConnectivityResolver extends BasicConfigurableObject implements ConnectivityResolver, EntityInitializer { - private static final Logger LOG = LoggerFactory.getLogger(BasicLocationNetworkInfoCustomizer.class); + private static final Logger LOG = LoggerFactory.getLogger(DefaultConnectivityResolver.class); public enum NetworkMode { /** @@ -114,8 +114,8 @@ public enum NetworkMode { ONLY_PRIVATE } - public static final ConfigKey MODE = ConfigKeys.newConfigKey(NetworkMode.class, - "mode", "Operation mode", NetworkMode.PREFER_PUBLIC); + public static final ConfigKey NETWORK_MODE = ConfigKeys.newConfigKey(NetworkMode.class, + "mode", "Operation mode: PREFER_PUBLIC, PREFER_PRIVATE, ONLY_PUBLIC or ONLY_PRIVATE"); @Beta public static final ConfigKey CHECK_CREDENTIALS = ConfigKeys.newBooleanConfigKey( @@ -130,30 +130,32 @@ public enum NetworkMode { // -------------------------------------------------------------------------------------- - public BasicLocationNetworkInfoCustomizer() { - super(); + public DefaultConnectivityResolver() { + this(ImmutableMap.of()); } - public BasicLocationNetworkInfoCustomizer(Map params) { - super(params); + public DefaultConnectivityResolver(Map params) { + this(ConfigBag.newInstance(params)); } - public BasicLocationNetworkInfoCustomizer(final ConfigBag params) { - super(params); + public DefaultConnectivityResolver(final ConfigBag params) { + for (Map.Entry entry : params.getAllConfig().entrySet()) { + config().set(ConfigKeys.newConfigKey(Object.class, entry.getKey()), entry.getValue()); + } } // -------------------------------------------------------------------------------------- /** - * Overrides the behaviour of {@link BasicJcloudsLocationCustomizer#apply(EntityLocal)} to set - * the instance as the value of {@link JcloudsLocationConfig#LOCATION_NETWORK_INFO_CUSTOMIZER}, - * rather than in its provisioning properties. + * Sets the instance as the value of {@link JcloudsLocationConfig#CONNECTIVITY_RESOLVER} + * in entity's provisioning properties. */ @Override public void apply(EntityLocal entity) { - ConfigKey subkey = BrooklynConfigKeys.PROVISIONING_PROPERTIES.subKey(JcloudsLocationConfig.LOCATION_NETWORK_INFO_CUSTOMIZER.getName()); + final String sensorName = JcloudsLocationConfig.CONNECTIVITY_RESOLVER.getName(); + ConfigKey subkey = BrooklynConfigKeys.PROVISIONING_PROPERTIES.subKey(sensorName); entity.config().set(subkey, this); - LOG.debug("{} set itself as the location network info customizer on {}", this, entity); + LOG.debug("{} set itself as the {} on {}", new Object[]{this, sensorName, entity}); } // -------------------------------------------------------------------------------------- @@ -165,23 +167,23 @@ public void apply(EntityLocal entity) { */ @Override public ManagementAddressResolveResult resolve( - JcloudsLocation location, NodeMetadata node, ConfigBag config, ManagementAddressResolveOptions options) { + JcloudsLocation location, NodeMetadata node, ConfigBag config, ConnectivityResolverOptions options) { LOG.debug("{} resolving management parameters for {}, node={}, config={}, options={}", new Object[]{this, location, node, config, options}); - Stopwatch timer = Stopwatch.createStarted(); + final Stopwatch timer = Stopwatch.createStarted(); // Should only be null in tests. final Entity contextEntity = getContextEntity(config); - if (shouldPublishNetworks() && options.publishNetworkSensors() && contextEntity != null) { + if (shouldPublishNetworks() && !options.isRebinding() && contextEntity != null) { publishNetworks(node, contextEntity); } HostAndPort hapChoice = null; LoginCredentials credChoice = null; - Iterable managementCandidates = getManagementCandidates(location, node, config, options); - Iterable credentialCandidates = getCredentialCandidates(location, node, options, config); + final Iterable managementCandidates = getManagementCandidates(location, node, config, options); + final Iterable credentialCandidates = getCredentialCandidates(location, node, options, config); // Try each pair of address and credential until one succeeds. - if (options.expectReachable() && options.pollForFirstReachableAddress() && shouldCheckCredentials()) { + if (shouldCheckCredentials() && options.pollForReachableAddresses()) { for (HostAndPort hap : managementCandidates) { for (LoginCredentials cred : credentialCandidates) { LOG.trace("Testing host={} with credential={}", hap, cred); @@ -193,25 +195,49 @@ public ManagementAddressResolveResult resolve( } if (hapChoice != null) break; } + } else if (shouldCheckCredentials()) { + LOG.debug("{} set on {} but pollForFirstReachableAddress={}", + new Object[]{CHECK_CREDENTIALS.getName(), this, options.pollForReachableAddresses()}); } if (hapChoice == null) { - LOG.trace("Choosing first management candidate given node={} and mode={}", node, getMode()); + LOG.trace("Choosing first management candidate given node={} and mode={}", node, getNetworkMode()); hapChoice = Iterables.getFirst(managementCandidates, null); } if (hapChoice == null) { - LOG.trace("Choosing first address of node={} in mode={}", node, getMode()); - final Iterator hit = getNodeAddressesWithMode(node).iterator(); + LOG.trace("Choosing first address of node={} in mode={}", node, getNetworkMode()); + final Iterator hit = getResolvableAddressesWithMode(node).iterator(); if (hit.hasNext()) HostAndPort.fromHost(hit.next()); } + if (hapChoice == null) { - throw new IllegalStateException("Exhausted all options when determining address for " + location); + throw new IllegalStateException("jclouds did not return any IP addresses matching " + getNetworkMode() + " in " + location); } - if (credChoice == null) { credChoice = Iterables.getFirst(credentialCandidates, null); if (credChoice == null) { - throw new IllegalStateException("Exhausted all options when determining credential for " + location); + throw new IllegalStateException("No credentials configured for " + location); + } + } + + // Treat AWS as a special case because the DNS fully qualified hostname in AWS is + // (normally?!) a good way to refer to the VM from both inside and outside of the region. + if (!isNetworkModeSet() && !options.isWindows()) { + final boolean lookupAwsHostname = Boolean.TRUE.equals(config.get(JcloudsLocationConfig.LOOKUP_AWS_HOSTNAME)); + String provider = config.get(JcloudsLocationConfig.CLOUD_PROVIDER); + if (provider == null) { + provider = location.getProvider(); + } + if (options.waitForConnectable() && "aws-ec2".equals(provider) && lookupAwsHostname) { + // getHostnameAws sshes to the machine and curls 169.254.169.254/latest/meta-data/public-hostname. + try { + LOG.debug("Resolving AWS hostname of {}", location); + String result = location.getHostnameAws(hapChoice, credChoice, config); + hapChoice = HostAndPort.fromParts(result, hapChoice.getPort()); + LOG.debug("Resolved AWS hostname of {}: {}", location, result); + } catch (Exception e) { + LOG.debug("Failed to resolve AWS hostname of " + location, e); + } } } @@ -228,22 +254,12 @@ private boolean shouldPublishNetworks() { return Boolean.TRUE.equals(config().get(PUBLISH_NETWORKS)); } - // TODO: Separate this into second part? void publishNetworks(NodeMetadata node, Entity entity) { - // todo hostnames? - int i = 0; - for (String address : node.getPrivateAddresses()) { - final AttributeSensor sensor = Sensors.newStringSensor("host.address.private." + i++); - if (entity.sensors().get(sensor) == null) { - entity.sensors().set(sensor, address); - } + if (entity.sensors().get(PRIVATE_ADDRESSES) == null) { + entity.sensors().set(PRIVATE_ADDRESSES, ImmutableSet.copyOf(node.getPrivateAddresses())); } - i = 0; - for (String address : node.getPublicAddresses()) { - final AttributeSensor sensor = Sensors.newStringSensor("host.address.public." + i++); - if (entity.sensors().get(sensor) == null) { - entity.sensors().set(sensor, address); - } + if (entity.sensors().get(PUBLIC_ADDRESSES) == null) { + entity.sensors().set(PUBLIC_ADDRESSES, ImmutableSet.copyOf(node.getPublicAddresses())); } } @@ -263,47 +279,31 @@ void publishNetworks(NodeMetadata node, Entity entity) { * */ protected Iterable getManagementCandidates( - JcloudsLocation location, NodeMetadata node, ConfigBag config, ManagementAddressResolveOptions options) { - final Optional hostAndPortOverride = options.getHostAndPortOverride(); - boolean lookupAwsHostname = Boolean.TRUE.equals(config.get(JcloudsLocation.LOOKUP_AWS_HOSTNAME)); - String provider = config.get(JcloudsLocation.CLOUD_PROVIDER); - if (provider == null) provider = location.getProvider(); - int defaultPort; - if (options.isWindows()) { - defaultPort = config.get(WinRmMachineLocation.USE_HTTPS_WINRM) ? 5986 : 5985; - } else { - defaultPort = node.getLoginPort(); - } + JcloudsLocation location, NodeMetadata node, ConfigBag config, ConnectivityResolverOptions options) { + final Optional portForwardSshOverride = options.portForwardSshOverride(); - // Will normally have come from port forwarding. - if (hostAndPortOverride.isPresent()) { + if (portForwardSshOverride.isPresent()) { // Don't try to resolve it; just use it - int port = hostAndPortOverride.get().hasPort() - ? hostAndPortOverride.get().getPort() - : defaultPort; - final HostAndPort override = HostAndPort.fromParts(hostAndPortOverride.get().getHostText(), port); - LOG.debug("Using host and port override for management candidates of {}: {}", location, override); + int port = portForwardSshOverride.get().hasPort() + ? portForwardSshOverride.get().getPort() + : options.defaultLoginPort(); + final HostAndPort override = HostAndPort.fromParts(portForwardSshOverride.get().getHostText(), port); + switch (getNetworkMode()) { + case ONLY_PRIVATE: + LOG.info("Ignoring mode {} in favour of port forwarding override for management candidates of {}: {}", + new Object[]{NetworkMode.ONLY_PRIVATE.name(), location, override}); + break; + default: + LOG.debug("Using host and port override for management candidates of {}: {}", location, override); + } return ImmutableList.of(override); } - // Treat AWS as a special case because the DNS fully qualified hostname in AWS is - // (normally?!) a good way to refer to the VM from both inside and outside of the region. - // TODO This is a bit weird: if the statement below is true then getHostnameAws will find the first - // reachable address, which repeats if case after this one. - if (options.expectReachable() && options.getUserCredentials().isPresent() && "aws-ec2".equals(provider) && lookupAwsHostname) { - // getHostnameAws sshes to the machine and curls 169.254.169.254/latest/meta-data/public-hostname. - Maybe result = location.getHostnameAws( - node, Optional.absent(), Suppliers.ofInstance(options.getUserCredentials().get()), config); - if (result.isPresent()) { - LOG.debug("Resolved AWS hostname for management candidates of {}: {}", location, result.get()); - return ImmutableList.of(HostAndPort.fromParts(result.get(), defaultPort)); - } - } - if (options.expectReachable() && options.pollForFirstReachableAddress() && options.getReachableAddressPredicate() != null) { + if (options.pollForReachableAddresses() && options.reachableAddressPredicate() != null) { LOG.debug("Using reachable addresses for management candidates of {}", location); try { - final Predicate predicate = options.getReachableAddressPredicate(); - return getReachableAddresses(node, predicate, options.getPollTimeout()); + final Predicate predicate = options.reachableAddressPredicate(); + return getReachableAddresses(node, predicate, options.reachableAddressTimeout()); } catch (RuntimeException e) { if (options.propagatePollForReachableFailure()) { throw Exceptions.propagate(e); @@ -312,16 +312,18 @@ protected Iterable getManagementCandidates( location.getCreationString(config), node); } } - } else if (options.expectReachable() && options.pollForFirstReachableAddress()) { - LOG.warn("{} was configured to expect node {} to be reachable and to poll for its first reachable " + - "address but the predicate to determine reachability was null", this, node); + } else if (options.pollForReachableAddresses()) { + throw new IllegalStateException(this + " was configured to expect " + node + " to be reachable " + + "and to poll for its reachable addresses but the predicate to determine reachability was null"); } - Iterable addresses = getNodeAddressesWithMode(node); + Iterable addresses = getResolvableAddressesWithMode(node); LOG.debug("Using first resolvable address in {} for management candidates of {}", Iterables.toString(addresses), location); for (String address : addresses) { if (isAddressResolvable(address)) { - return ImmutableList.of(HostAndPort.fromParts(address, defaultPort)); + return ImmutableList.of(HostAndPort.fromParts(address, options.defaultLoginPort())); + } else { + LOG.debug("Unresolvable address: " + address); } } @@ -329,7 +331,7 @@ protected Iterable getManagementCandidates( new Object[]{addresses, location.getCreationString(config), node}); String host = Iterables.getFirst(addresses, null); if (host != null) { - return ImmutableList.of(HostAndPort.fromParts(host, defaultPort)); + return ImmutableList.of(HostAndPort.fromParts(host, options.defaultLoginPort())); } else { return ImmutableList.of(); } @@ -337,29 +339,35 @@ protected Iterable getManagementCandidates( /** * Returns all reachable addresses according to reachablePredicate. - * Iterators are ordered according to the configured {@link #getMode() mode}. + * Iterators are ordered according to the configured {@link #getNetworkMode() mode}. */ protected Iterable getReachableAddresses(NodeMetadata node, Predicate reachablePredicate, Duration timeout) { if (timeout == null) timeout = Duration.FIVE_MINUTES; - Iterable candidates = getNodeAddressesWithMode(node); + Iterable candidates = getResolvableAddressesWithMode(node); return JcloudsUtil.getReachableAddresses(candidates, node.getLoginPort(), timeout, reachablePredicate); } - protected Iterable getNodeAddressesWithMode(NodeMetadata node) { - switch (getMode()) { + protected Iterable getResolvableAddressesWithMode(NodeMetadata node) { + Iterable base; + switch (getNetworkMode()) { case ONLY_PRIVATE: - return node.getPrivateAddresses(); + base = node.getPrivateAddresses(); + break; case ONLY_PUBLIC: - return node.getPublicAddresses(); + base = node.getPublicAddresses(); + break; case PREFER_PRIVATE: - return Iterables.concat(node.getPrivateAddresses(), node.getPublicAddresses()); + base = Iterables.concat(node.getPrivateAddresses(), node.getPublicAddresses()); + break; case PREFER_PUBLIC: default: - return Iterables.concat(node.getPublicAddresses(), node.getPrivateAddresses()); + base = Iterables.concat(node.getPublicAddresses(), node.getPrivateAddresses()); } + return FluentIterable.from(base) + .filter(new AddressResolvable()); } - protected boolean isAddressResolvable(String addr) { + protected static boolean isAddressResolvable(String addr) { try { Networking.getInetAddressWithFixedName(addr); return true; // fine, it resolves @@ -369,6 +377,13 @@ protected boolean isAddressResolvable(String addr) { } } + private static class AddressResolvable implements Predicate { + @Override + public boolean apply(@Nullable String input) { + return isAddressResolvable(input); + } + } + // -------------------------------------------------------------------------------------- protected boolean shouldCheckCredentials() { @@ -391,45 +406,46 @@ protected boolean checkCredential( } protected Iterable getCredentialCandidates( - JcloudsLocation location, NodeMetadata node, ManagementAddressResolveOptions options, ConfigBag setup) { + JcloudsLocation location, NodeMetadata node, ConnectivityResolverOptions options, ConfigBag setup) { LoginCredentials userCredentials = null; // Figure out which login credentials to use. We only make a connection with // initialCredentials when jclouds didn't do any sshing and wait for connectable is true. // 0. if jclouds didn't do anything and we should wait for the machine then initial credentials is // whatever waitForSshable determines and then create the user ourselves. - if (options.skipJcloudsSshing() && options.expectReachable()) { - if (options.isWindows()) { - return ImmutableList.of(options.getInitialCredentials()); + if (options.skipJcloudsSshing() && options.waitForConnectable()) { + if (options.isWindows() && options.initialCredentials().isPresent()) { + return ImmutableList.of(options.initialCredentials().get()); } else { return location.generateCredentials(node.getCredentials(), setup.get(JcloudsLocationConfig.LOGIN_USER)); } } // 1. Were they configured by the user? - LoginCredentials customCredentials = setup.get(JcloudsLocation.CUSTOM_CREDENTIALS); + LoginCredentials customCredentials = setup.get(JcloudsLocationConfig.CUSTOM_CREDENTIALS); if (customCredentials != null) { userCredentials = customCredentials; //set userName and other data, from these credentials - Object oldUsername = setup.put(JcloudsLocation.USER, customCredentials.getUser()); + Object oldUsername = setup.put(JcloudsLocationConfig.USER, customCredentials.getUser()); LOG.debug("Using username {}, from custom credentials, on node {}. User was previously {}", new Object[]{customCredentials.getUser(), node, oldUsername}); if (customCredentials.getOptionalPassword().isPresent()) { - setup.put(JcloudsLocation.PASSWORD, customCredentials.getOptionalPassword().get()); + setup.put(JcloudsLocationConfig.PASSWORD, customCredentials.getOptionalPassword().get()); } if (customCredentials.getOptionalPrivateKey().isPresent()) { - setup.put(JcloudsLocation.PRIVATE_KEY_DATA, customCredentials.getOptionalPrivateKey().get()); + setup.put(JcloudsLocationConfig.PRIVATE_KEY_DATA, customCredentials.getOptionalPrivateKey().get()); } } // 2. Can they be extracted from the setup+node? - if (userCredentials == null || - (!userCredentials.getOptionalPassword().isPresent() && !userCredentials.getOptionalPrivateKey().isPresent())) { + if ((userCredentials == null || + (!userCredentials.getOptionalPassword().isPresent() && !userCredentials.getOptionalPrivateKey().isPresent())) && + options.initialCredentials().isPresent()) { // We either don't have any userCredentials, or it is missing both a password/key. if (userCredentials != null) { LOG.debug("Custom credential from {} is missing both password and private key; " + - "extracting them from the VM: {}", JcloudsLocation.CUSTOM_CREDENTIALS.getName(), userCredentials); + "extracting them from the VM: {}", JcloudsLocationConfig.CUSTOM_CREDENTIALS.getName(), userCredentials); } // TODO See waitForSshable, which now handles if the node.getLoginCredentials has both a password+key - userCredentials = location.extractVmCredentials(setup, node, options.getInitialCredentials()); + userCredentials = location.extractVmCredentials(setup, node, options.initialCredentials().get()); } if (userCredentials == null) { // only happens if something broke above... @@ -455,18 +471,20 @@ protected Entity getContextEntity(ConfigBag configBag) { return null; } - protected NetworkMode getMode() { - NetworkMode networkMode = config().get(MODE); - return networkMode != null ? networkMode : MODE.getDefaultValue(); + protected NetworkMode getNetworkMode() { + NetworkMode networkMode = config().get(NETWORK_MODE); + return networkMode != null ? networkMode : NetworkMode.PREFER_PUBLIC; + } + + private boolean isNetworkModeSet() { + return config().get(NETWORK_MODE) != null; } @Override public String toString() { return MoreObjects.toStringHelper(this) - .add("mode", getMode()) + .add("mode", getNetworkMode()) .toString(); } - // -------------------------------------------------------------------------------------- - } diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java index db68512ffaa..8e25d64f242 100644 --- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java @@ -430,9 +430,9 @@ public Collection getCustomizers(ConfigBag setup) { return result; } - public LocationNetworkInfoCustomizer getLocationNetworkInfoCustomizer(ConfigBag setup) { - LocationNetworkInfoCustomizer configured = setup.get(LOCATION_NETWORK_INFO_CUSTOMIZER); - return (configured != null) ? configured : new BasicLocationNetworkInfoCustomizer(); + public ConnectivityResolver getLocationNetworkInfoCustomizer(ConfigBag setup) { + ConnectivityResolver configured = setup.get(CONNECTIVITY_RESOLVER); + return (configured != null) ? configured : new DefaultConnectivityResolver(); } protected Collection getMachineCustomizers(ConfigBag setup) { @@ -631,30 +631,31 @@ public MachineLocation obtain(Map flags) throws NoMachinesAvailableExceptio } } - protected ManagementAddressResolveOptions getManagementAddressResolveOptions( - NodeMetadata node, ConfigBag setup, Optional sshHostAndPortOverride, Optional userCredentials) { - boolean waitForSshable = !"false".equalsIgnoreCase(setup.get(WAIT_FOR_SSHABLE)); - boolean waitForWinRmable = !"false".equalsIgnoreCase(setup.get(WAIT_FOR_WINRM_AVAILABLE)); - boolean usePortForwarding = setup.get(USE_PORT_FORWARDING); - boolean skipJcloudsSshing = Boolean.FALSE.equals(setup.get(USE_JCLOUDS_SSH_INIT)) || usePortForwarding; - boolean windows = isWindows(node, setup); - boolean waitForConnectable = windows ? waitForWinRmable : waitForSshable; - String pollForFirstReachable = setup.get(POLL_FOR_FIRST_REACHABLE_ADDRESS); + protected ConnectivityResolverOptions.Builder getConnectivityOptionsBuilder(ConfigBag setup, boolean isWindows) { + boolean waitForSshable = !"false".equalsIgnoreCase(setup.get(JcloudsLocationConfig.WAIT_FOR_SSHABLE)); + boolean waitForWinRmable = !"false".equalsIgnoreCase(setup.get(JcloudsLocationConfig.WAIT_FOR_WINRM_AVAILABLE)); + boolean waitForConnectable = isWindows ? waitForWinRmable : waitForSshable; + + boolean usePortForwarding = setup.get(JcloudsLocationConfig.USE_PORT_FORWARDING); + boolean skipJcloudsSshing = usePortForwarding || + Boolean.FALSE.equals(setup.get(JcloudsLocationConfig.USE_JCLOUDS_SSH_INIT)); + + ConnectivityResolverOptions.Builder builder = ConnectivityResolverOptions.builder() + .waitForConnectable(waitForConnectable) + .usePortForwarding(usePortForwarding) + .skipJcloudsSshing(skipJcloudsSshing); + + String pollForFirstReachable = setup.get(JcloudsLocationConfig.POLL_FOR_FIRST_REACHABLE_ADDRESS); boolean pollEnabled = !"false".equalsIgnoreCase(pollForFirstReachable); - Duration pollTimeout = "true".equals(pollForFirstReachable) ? Duration.FIVE_MINUTES : Duration.of(pollForFirstReachable); - Predicate reachablePredicate = getReachableAddressesPredicate(setup); - return new ManagementAddressResolveOptions() - .expectReachable(waitForConnectable) - .hostAndPortOverride(sshHostAndPortOverride) - .initialCredentials(node.getCredentials()) - .isWindows(windows) - .pollForFirstReachableAddress(pollEnabled) - .pollTimeout(pollTimeout) - .propagatePollForReachableFailure(true) - .reachableAddressPredicate(reachablePredicate) - .skipJcloudsSshing(skipJcloudsSshing) - .userCredentials(userCredentials) - .waitForSshable(waitForSshable); + + if (pollEnabled) { + Predicate reachableAddressesPredicate = getReachableAddressesPredicate(setup); + Duration pollTimeout = "true".equals(pollForFirstReachable) + ? Duration.FIVE_MINUTES + : Duration.of(pollForFirstReachable); + builder.pollForReachableAddresses(reachableAddressesPredicate, pollTimeout, true); + } + return builder; } protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailableException { @@ -663,12 +664,13 @@ protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailable throw new IllegalStateException("Access controller forbids provisioning in "+this+": "+access.getMsg()); } - boolean waitForSshable = !"false".equalsIgnoreCase(setup.get(WAIT_FOR_SSHABLE)); - boolean waitForWinRmable = !"false".equalsIgnoreCase(setup.get(WAIT_FOR_WINRM_AVAILABLE)); - boolean usePortForwarding = setup.get(USE_PORT_FORWARDING); - boolean skipJcloudsSshing = Boolean.FALSE.equals(setup.get(USE_JCLOUDS_SSH_INIT)) || usePortForwarding; + Predicate reachablePredicate = getReachableAddressesPredicate(setup); + ConnectivityResolverOptions options = getConnectivityOptionsBuilder(setup, false).build(); + + // FIXME How do we influence the node.getLoginPort, so it is set correctly for Windows? + // Setup port-forwarding, if required JcloudsPortForwarderExtension portForwarder = setup.get(PORT_FORWARDER); - if (usePortForwarding) checkNotNull(portForwarder, "portForwarder, when use-port-forwarding enabled"); + if (options.usePortForwarding()) checkNotNull(portForwarder, "portForwarder, when use-port-forwarding enabled"); final ComputeService computeService = getComputeService(setup); CloudMachineNamer cloudMachineNamer = getCloudMachineNamer(setup); @@ -707,12 +709,14 @@ protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailable // Setup the template template = buildTemplate(computeService, setup, customizers); boolean expectWindows = isWindows(template, setup); - if (!skipJcloudsSshing) { + if (!options.skipJcloudsSshing()) { if (expectWindows) { // TODO Was this too early to look at template.getImage? e.g. customizeTemplate could subsequently modify it. LOG.warn("Ignoring invalid configuration for Windows provisioning of "+template.getImage()+": "+USE_JCLOUDS_SSH_INIT.getName()+" should be false"); - skipJcloudsSshing = true; - } else if (waitForSshable) { + options = options.toBuilder() + .skipJcloudsSshing(true) + .build(); + } else if (options.waitForConnectable()) { userCredentials = initTemplateForCreateUser(template, setup); } } @@ -754,7 +758,6 @@ protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailable throw new IllegalStateException("No nodes returned by jclouds create-nodes in " + getCreationString(setup)); boolean windows = isWindows(node, setup); - boolean waitForConnectable = (windows) ? waitForWinRmable : waitForSshable; if (windows) { int newLoginPort = node.getLoginPort() == 22 @@ -770,31 +773,35 @@ protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailable .credentials(LoginCredentials.builder(node.getCredentials()).user(newLoginUser).build()) .build(); } - // FIXME How do we influence the node.getLoginPort, so it is set correctly for Windows? - // Setup port-forwarding, if required - Optional sshHostAndPortOverride; - if (usePortForwarding) { - sshHostAndPortOverride = Optional.of(portForwarder.openPortForwarding( + Optional portForwardSshOverride; + if (options.usePortForwarding()) { + portForwardSshOverride = Optional.of(portForwarder.openPortForwarding( node, node.getLoginPort(), Optional.absent(), Protocol.TCP, Cidr.UNIVERSAL)); } else { - sshHostAndPortOverride = Optional.absent(); + portForwardSshOverride = Optional.absent(); } - ManagementAddressResolveOptions resolveOptions = getManagementAddressResolveOptions( - node, setup, sshHostAndPortOverride, Optional.fromNullable(userCredentials)); - LocationNetworkInfoCustomizer networkInfoCustomizer = getLocationNetworkInfoCustomizer(setup); + options = options.toBuilder() + .isWindows(windows) + .defaultLoginPort(node.getLoginPort()) + .portForwardSshOverride(portForwardSshOverride.orNull()) + .initialCredentials(node.getCredentials()) + .userCredentials(userCredentials) + .build(); + + ConnectivityResolver networkInfoCustomizer = getLocationNetworkInfoCustomizer(setup); - ManagementAddressResolveResult hapandc = networkInfoCustomizer.resolve(this, node, setup, resolveOptions); - final HostAndPort managementHostAndPort = hapandc.hostAndPort(); - LoginCredentials creds = hapandc.credentials(); + ManagementAddressResolveResult hostPortCred = networkInfoCustomizer.resolve(this, node, setup, options); + final HostAndPort managementHostAndPort = hostPortCred.hostAndPort(); + LoginCredentials creds = hostPortCred.credentials(); LOG.info("Using host-and-port={} and user={} when connecting to {}", new Object[]{managementHostAndPort, creds.getUser(), node}); - if (skipJcloudsSshing && waitForConnectable) { + if (options.skipJcloudsSshing() && options.waitForConnectable()) { LoginCredentials createdCredentials = createUser(computeService, node, managementHostAndPort, creds, setup); if (createdCredentials != null) { userCredentials = createdCredentials; @@ -809,12 +816,11 @@ protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailable putIfPresentButDifferent(setup, JcloudsLocationConfig.PRIVATE_KEY_DATA, userCredentials.getOptionalPrivateKey().orNull()); // Wait for the VM to be reachable over SSH - // TODO: this has already been tested by locationnetworkinfocustomizer - if (waitForSshable && !windows) { + if (options.waitForConnectable() && !options.isWindows()) { waitForSshable(computeService, node, managementHostAndPort, ImmutableList.of(userCredentials), setup); } else { - LOG.debug("Skipping ssh check for {} ({}) due to config waitForSshable={}, windows={}", - new Object[]{node, getCreationString(setup), waitForSshable, windows}); + LOG.debug("Skipping ssh check for {} ({}) due to config waitForConnectable={}, windows={}", + new Object[]{node, getCreationString(setup), options.waitForConnectable(), windows}); } // Do not store the credentials on the node as this may leak the credentials if they @@ -836,9 +842,9 @@ protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailable portForwardManager = (PortForwardManager) getManagementContext().getLocationRegistry().getLocationManaged(PortForwardManagerLocationResolver.PFM_GLOBAL_SPEC); } - if (usePortForwarding && sshHostAndPortOverride.isPresent()) { + if (options.usePortForwarding() && portForwardSshOverride.isPresent()) { // Now that we have the sshMachineLocation, we can associate the port-forwarding address with it. - portForwardManager.associate(node.getId(), sshHostAndPortOverride.get(), machineLocation, node.getLoginPort()); + portForwardManager.associate(node.getId(), portForwardSshOverride.get(), machineLocation, node.getLoginPort()); } if ("docker".equals(this.getProvider())) { @@ -855,7 +861,7 @@ protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailable List customisationForLogging = new ArrayList(); // Apply same securityGroups rules to iptables, if iptables is running on the node - if (waitForSshable) { + if (options.waitForConnectable()) { String setupScript = setup.get(JcloudsLocationConfig.CUSTOM_MACHINE_SETUP_SCRIPT_URL); List setupScripts = setup.get(JcloudsLocationConfig.CUSTOM_MACHINE_SETUP_SCRIPT_URL_LIST); @@ -1823,9 +1829,12 @@ protected JcloudsMachineLocation registerMachineLocation(ConfigBag setup, NodeMe boolean windows = isWindows(node, setup); // Not publishing networks since they should have previously been published. - ManagementAddressResolveOptions options = getManagementAddressResolveOptions( - node, setup, Optional.absent(), Optional.fromNullable(node.getCredentials())) - .publishNetworks(false); + ConnectivityResolverOptions options = getConnectivityOptionsBuilder(setup, windows) + .initialCredentials(node.getCredentials()) + .userCredentials(node.getCredentials()) + .defaultLoginPort(node.getLoginPort()) + .isRebinding(true) + .build(); HostAndPort managementHostAndPort = getLocationNetworkInfoCustomizer(setup) .resolve(this, node, setup, options) .hostAndPort(); @@ -2556,7 +2565,6 @@ Iterable generateCredentials(LoginCredentials nodeCreds, @Null users.add(loginUserOverride); } List credentialsToTry = new ArrayList<>(); - Lists.newArrayList(); for (String user : users) { if (nodeCreds.getOptionalPassword().isPresent() && nodeCreds.getOptionalPrivateKey().isPresent()) { credentialsToTry.add(LoginCredentials.builder(nodeCreds).noPassword().user(user).build()); @@ -2913,7 +2921,7 @@ Maybe getHostnameAws(NodeMetadata node, Optional sshHostAnd } if (sshHostAndPort.isPresent() || inferredHostAndPort != null) { if (isWindows(node, setup)) { - LOG.warn("Error querying aws-ec2 Windows instance "+node.getId()+"@"+node.getLocation()+" over ssh for its hostname; falling back to jclouds metadata for address"); + LOG.warn("Cannot query aws-ec2 Windows instance "+node.getId()+"@"+node.getLocation()+" over ssh for its hostname; falling back to jclouds metadata for address"); } else { HostAndPort hostAndPortToUse = sshHostAndPort.isPresent() ? sshHostAndPort.get() : inferredHostAndPort; try { @@ -2926,7 +2934,7 @@ Maybe getHostnameAws(NodeMetadata node, Optional sshHostAnd return Maybe.absent(); } - private String getHostnameAws(HostAndPort hostAndPort, LoginCredentials userCredentials, ConfigBag setup) { + String getHostnameAws(HostAndPort hostAndPort, LoginCredentials userCredentials, ConfigBag setup) { SshMachineLocation sshLocByIp = null; try { // TODO messy way to get an SSH session diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationConfig.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationConfig.java index b1c2557f453..d06f7ccc6de 100644 --- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationConfig.java +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationConfig.java @@ -211,9 +211,9 @@ public interface JcloudsLocationConfig extends CloudLocationConfig { "customizersSupplierType", "Optional type of a Supplier> " + "(to be class-loaded and constructed with either a ConfigBag or no-arg constructor)"); - ConfigKey LOCATION_NETWORK_INFO_CUSTOMIZER = ConfigKeys.newConfigKey(LocationNetworkInfoCustomizer.class, - "locationNetworkInfoCustomizer", - "Optional LocationNetworkInfoCustomizer to control the host and port and credentials used to connect to instances."); + ConfigKey CONNECTIVITY_RESOLVER = ConfigKeys.newConfigKey(ConnectivityResolver.class, + "connectivityResolver", + "Optional instance of a ConnectivityResolver that the location will use in favour of " + DefaultConnectivityResolver.class.getSimpleName()); public static final ConfigKey LOCAL_TEMP_DIR = SshTool.PROP_LOCAL_TEMP_DIR; diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ManagementAddressResolveOptions.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ManagementAddressResolveOptions.java deleted file mode 100644 index 9918f4be820..00000000000 --- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ManagementAddressResolveOptions.java +++ /dev/null @@ -1,158 +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 org.apache.brooklyn.location.jclouds; - -import org.apache.brooklyn.util.time.Duration; -import org.jclouds.domain.LoginCredentials; - -import com.google.common.annotations.Beta; -import com.google.common.base.Optional; -import com.google.common.base.Predicate; -import com.google.common.net.HostAndPort; - -/** - * Holds parameters to be used by a {@link LocationNetworkInfoCustomizer}. - */ -@Beta -public class ManagementAddressResolveOptions { - - private Duration pollTimeout; - private boolean waitForSshable; - private boolean pollForFirstReachableAddress; - private boolean expectReachable; - private boolean isWindows; - private boolean propagatePollForReachableFailure; - private LoginCredentials initialCredentials; - private Optional userCredentials = Optional.absent(); - private Optional hostAndPortOverride = Optional.absent(); - private boolean skipJcloudsSshing; - private boolean publishNetworkSensors = true; - private Predicate reachableAddressPredicate; - - public ManagementAddressResolveOptions expectReachable(boolean expectConnectable) { - this.expectReachable = expectConnectable; - return this; - } - - /** Indicate the host and port that should be used over all others. Normally used in tandem with a port forwarder. */ - public ManagementAddressResolveOptions hostAndPortOverride(Optional hostAndPortOverride) { - this.hostAndPortOverride = hostAndPortOverride; - return this; - } - - public ManagementAddressResolveOptions initialCredentials(LoginCredentials initialCredentials) { - this.initialCredentials = initialCredentials; - return this; - } - - public ManagementAddressResolveOptions pollForFirstReachableAddress(boolean pollForFirstReachableAddress) { - this.pollForFirstReachableAddress = pollForFirstReachableAddress; - return this; - } - - public ManagementAddressResolveOptions propagatePollForReachableFailure(boolean propagatePollForReachableFailure) { - this.propagatePollForReachableFailure = propagatePollForReachableFailure; - return this; - } - - public ManagementAddressResolveOptions pollTimeout(Duration pollTimeout) { - this.pollTimeout = pollTimeout; - return this; - } - - public ManagementAddressResolveOptions skipJcloudsSshing(boolean skipJcloudsSshing) { - this.skipJcloudsSshing = skipJcloudsSshing; - return this; - } - - public ManagementAddressResolveOptions waitForSshable(boolean waitForSshable) { - this.waitForSshable = waitForSshable; - return this; - } - - public ManagementAddressResolveOptions isWindows(boolean windows) { - isWindows = windows; - return this; - } - - public ManagementAddressResolveOptions userCredentials(Optional userCredentials) { - this.userCredentials = userCredentials; - return this; - } - - public ManagementAddressResolveOptions publishNetworks(boolean publishNetworks) { - this.publishNetworkSensors = publishNetworks; - return this; - } - - public ManagementAddressResolveOptions reachableAddressPredicate(Predicate predicate) { - this.reachableAddressPredicate = predicate; - return this; - } - - public Duration getPollTimeout() { - return pollTimeout; - } - - public boolean waitForSshable() { - return waitForSshable; - } - - public boolean pollForFirstReachableAddress() { - return pollForFirstReachableAddress; - } - - public boolean expectReachable() { - return expectReachable; - } - - public boolean isWindows() { - return isWindows; - } - - public boolean propagatePollForReachableFailure() { - return propagatePollForReachableFailure; - } - - public LoginCredentials getInitialCredentials() { - return initialCredentials; - } - - public Optional getUserCredentials() { - return userCredentials; - } - - public Optional getHostAndPortOverride() { - return hostAndPortOverride; - } - - public boolean skipJcloudsSshing() { - return skipJcloudsSshing; - } - - public boolean publishNetworkSensors() { - return publishNetworkSensors; - } - - public Predicate getReachableAddressPredicate() { - return reachableAddressPredicate; - } - -} diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsStubbedLiveTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsStubbedLiveTest.java index 3190d3365dc..928445988f7 100644 --- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsStubbedLiveTest.java +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsStubbedLiveTest.java @@ -80,7 +80,8 @@ protected JcloudsLocation replaceJcloudsLocation(String locationSpec) { locationSpec, jcloudsLocationConfig(ImmutableMap.of( JcloudsLocationConfig.COMPUTE_SERVICE_REGISTRY, computeServiceRegistry, - JcloudsLocationConfig.WAIT_FOR_SSHABLE, "false"))); + JcloudsLocationConfig.WAIT_FOR_SSHABLE, "false", + JcloudsLocationConfig.POLL_FOR_FIRST_REACHABLE_ADDRESS, "false"))); return jcloudsLocation; } diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BasicLocationNetworkInfoInitializerTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BasicLocationNetworkInfoInitializerTest.java index 45e24e398e5..51fcad23c2f 100644 --- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BasicLocationNetworkInfoInitializerTest.java +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BasicLocationNetworkInfoInitializerTest.java @@ -34,11 +34,11 @@ public class BasicLocationNetworkInfoInitializerTest extends BrooklynAppUnitTest @Test public void testInitializerSetsConfigKeyOnEntity() { TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class) - .addInitializer(BasicLocationNetworkInfoCustomizer.class)); - final ConfigKey key = BrooklynConfigKeys.PROVISIONING_PROPERTIES.subKey(JcloudsLocationConfig.LOCATION_NETWORK_INFO_CUSTOMIZER.getName()); + .addInitializer(DefaultConnectivityResolver.class)); + final ConfigKey key = BrooklynConfigKeys.PROVISIONING_PROPERTIES.subKey(JcloudsLocationConfig.CONNECTIVITY_RESOLVER.getName()); final Object value = entity.config().get(key); assertNotNull(value, "no value on " + entity + " for " + key); - assertEquals(value.getClass(), BasicLocationNetworkInfoCustomizer.class); + assertEquals(value.getClass(), DefaultConnectivityResolver.class); } } diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BasicLocationNetworkInfoCustomizerTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/DefaultConnectivityResolverTest.java similarity index 78% rename from locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BasicLocationNetworkInfoCustomizerTest.java rename to locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/DefaultConnectivityResolverTest.java index 772318d9774..33a019ae751 100644 --- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/BasicLocationNetworkInfoCustomizerTest.java +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/DefaultConnectivityResolverTest.java @@ -25,7 +25,7 @@ import java.util.Set; -import org.apache.brooklyn.location.jclouds.BasicLocationNetworkInfoCustomizer.NetworkMode; +import org.apache.brooklyn.location.jclouds.DefaultConnectivityResolver.NetworkMode; import org.apache.brooklyn.util.core.config.ConfigBag; import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool; import org.apache.brooklyn.util.core.internal.winrm.RecordingWinRmTool; @@ -36,7 +36,6 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import com.google.common.base.Optional; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -44,7 +43,7 @@ import com.google.common.collect.Sets; import com.google.common.net.HostAndPort; -public class BasicLocationNetworkInfoCustomizerTest extends AbstractJcloudsStubbedUnitTest { +public class DefaultConnectivityResolverTest extends AbstractJcloudsStubbedUnitTest { private final LoginCredentials credential = LoginCredentials.builder().user("AzureDiamond").password("hunter2").build(); @@ -52,9 +51,10 @@ public class BasicLocationNetworkInfoCustomizerTest extends AbstractJcloudsStubb public void testRespectsHostAndPortOverride() throws Exception { initNodeCreatorAndJcloudsLocation(newNodeCreator(), ImmutableMap.of()); // ideally would confirm that no credentials are tested either. - ManagementAddressResolveOptions options = newResolveOptions() - .hostAndPortOverride(Optional.of(HostAndPort.fromParts("10.1.1.4", 4361))); - BasicLocationNetworkInfoCustomizer customizer = new BasicLocationNetworkInfoCustomizer(); + ConnectivityResolverOptions options = newResolveOptions() + .portForwardSshOverride(HostAndPort.fromParts("10.1.1.4", 4361)) + .build(); + DefaultConnectivityResolver customizer = new DefaultConnectivityResolver(); ManagementAddressResolveResult result = customizer.resolve(jcloudsLocation, newNodeMetadata(), ConfigBag.newInstance(), options); assertEquals(result.hostAndPort().getHostText(), "10.1.1.4"); assertEquals(result.hostAndPort().getPort(), 4361); @@ -66,15 +66,16 @@ public void testObtainsHostnameFromAwsMachine() throws Exception { RecordingSshTool.setCustomResponse(".*curl.*169.254.169.254.*", new RecordingSshTool.CustomResponse(0, expectedHostname, "")); initNodeCreatorAndJcloudsLocation(newNodeCreator(), ImmutableMap.of( JcloudsLocationConfig.LOOKUP_AWS_HOSTNAME, true)); - ManagementAddressResolveOptions options = newResolveOptions() - .reachableAddressPredicate(Predicates.alwaysTrue()) - .userCredentials(Optional.of(credential)); - BasicLocationNetworkInfoCustomizer customizer = new BasicLocationNetworkInfoCustomizer(); + ConnectivityResolverOptions options = newResolveOptions() + .waitForConnectable(true) + .pollForReachableAddresses(Predicates.alwaysTrue(), Duration.ONE_SECOND, true) + .userCredentials(credential) + .build(); + DefaultConnectivityResolver customizer = new DefaultConnectivityResolver(); ConfigBag configBag = ConfigBag.newInstance(); - Iterable candidates = customizer.getManagementCandidates(jcloudsLocation, newNodeMetadata(), configBag, options); - - HostAndPort choice = candidates.iterator().next(); - assertEquals(choice.getHostText(), expectedHostname); + ManagementAddressResolveResult result = customizer.resolve( + jcloudsLocation, newNodeMetadata(), configBag, options); + assertEquals(result.hostAndPort().getHostText(), expectedHostname); } @Test @@ -89,7 +90,7 @@ public RecordingSshTool.CustomResponse generate(RecordingSshTool.ExecParams exec } }); initNodeCreatorAndJcloudsLocation(newNodeCreator(), ImmutableMap.of()); - BasicLocationNetworkInfoCustomizer customizer = new BasicLocationNetworkInfoCustomizer(); + DefaultConnectivityResolver customizer = new DefaultConnectivityResolver(); final ConfigBag config = ConfigBag.newInstance(ImmutableMap.of( JcloudsLocationConfig.WAIT_FOR_SSHABLE, "1ms")); assertTrue(customizer.checkCredential( @@ -111,7 +112,7 @@ public RecordingWinRmTool.CustomResponse generate(RecordingWinRmTool.ExecParams } }); initNodeCreatorAndJcloudsLocation(newNodeCreator(), ImmutableMap.of()); - BasicLocationNetworkInfoCustomizer customizer = new BasicLocationNetworkInfoCustomizer(); + DefaultConnectivityResolver customizer = new DefaultConnectivityResolver(); final ConfigBag config = ConfigBag.newInstance(ImmutableMap.of( JcloudsLocationConfig.WAIT_FOR_WINRM_AVAILABLE, "1ms")); assertTrue(customizer.checkCredential( @@ -144,14 +145,16 @@ public RecordingSshTool.CustomResponse generate(RecordingSshTool.ExecParams exec } }); ConfigBag config = ConfigBag.newInstance(ImmutableMap.of( + JcloudsLocationConfig.LOOKUP_AWS_HOSTNAME, false, JcloudsLocationConfig.WAIT_FOR_SSHABLE, "1ms", JcloudsLocation.CUSTOM_CREDENTIALS, credential)); - ManagementAddressResolveOptions options = newResolveOptions() - .reachableAddressPredicate(Predicates.in(reachableIps)); + ConnectivityResolverOptions options = newResolveOptions() + .pollForReachableAddresses(Predicates.in(reachableIps), Duration.ONE_SECOND, true) + .build(); // Chooses authorisedHostAndPort when credentials are tested. - BasicLocationNetworkInfoCustomizer customizer = new BasicLocationNetworkInfoCustomizer(ImmutableMap.of( - BasicLocationNetworkInfoCustomizer.CHECK_CREDENTIALS, true)); + DefaultConnectivityResolver customizer = new DefaultConnectivityResolver(ImmutableMap.of( + DefaultConnectivityResolver.CHECK_CREDENTIALS, true)); ManagementAddressResolveResult result = customizer.resolve(jcloudsLocation, newNodeMetadata(), config, options); assertEquals(result.hostAndPort(), authorisedHostAndPort); @@ -159,8 +162,8 @@ public RecordingSshTool.CustomResponse generate(RecordingSshTool.ExecParams exec // Chooses otherHostAndPort when credentials aren't tested. RecordingSshTool.clear(); - customizer = new BasicLocationNetworkInfoCustomizer(ImmutableMap.of( - BasicLocationNetworkInfoCustomizer.CHECK_CREDENTIALS, false)); + customizer = new DefaultConnectivityResolver(ImmutableMap.of( + DefaultConnectivityResolver.CHECK_CREDENTIALS, false)); result = customizer.resolve(jcloudsLocation, newNodeMetadata(), config, options); assertEquals(result.hostAndPort(), otherHostAndPort); assertTrue(RecordingSshTool.getExecCmds().isEmpty(), @@ -182,15 +185,16 @@ public Object[][] testModeDataProvider() throws Exception { @Test(dataProvider = "testModeDataProvider") public void testMode(NetworkMode mode, Set reachableIps, String expectedIp) throws Exception { - final BasicLocationNetworkInfoCustomizer customizer = new BasicLocationNetworkInfoCustomizer(ImmutableMap.of( - BasicLocationNetworkInfoCustomizer.MODE, mode, - BasicLocationNetworkInfoCustomizer.CHECK_CREDENTIALS, false)); + final DefaultConnectivityResolver customizer = new DefaultConnectivityResolver(ImmutableMap.of( + DefaultConnectivityResolver.NETWORK_MODE, mode, + DefaultConnectivityResolver.CHECK_CREDENTIALS, false)); initNodeCreatorAndJcloudsLocation(newNodeCreator(), ImmutableMap.of( - JcloudsLocationConfig.LOCATION_NETWORK_INFO_CUSTOMIZER, customizer)); + JcloudsLocationConfig.CONNECTIVITY_RESOLVER, customizer)); - ManagementAddressResolveOptions options = newResolveOptions() - .reachableAddressPredicate(Predicates.in(reachableIps)); + ConnectivityResolverOptions options = newResolveOptions() + .pollForReachableAddresses(Predicates.in(reachableIps), Duration.ONE_SECOND, true) + .build(); ConfigBag configBag = ConfigBag.newInstance(); ManagementAddressResolveResult result = customizer.resolve(jcloudsLocation, newNodeMetadata(), configBag, options); @@ -212,24 +216,22 @@ public Object[][] testFallibleModesDataProvider() throws Exception { */ @Test(dataProvider = "fallibleModes", expectedExceptions = IllegalStateException.class) public void testModeUnavailable(NetworkMode mode, Set reachableIps) throws Exception { - final BasicLocationNetworkInfoCustomizer customizer = new BasicLocationNetworkInfoCustomizer(ImmutableMap.of( - BasicLocationNetworkInfoCustomizer.MODE, mode, - BasicLocationNetworkInfoCustomizer.CHECK_CREDENTIALS, false)); + final DefaultConnectivityResolver customizer = new DefaultConnectivityResolver(ImmutableMap.of( + DefaultConnectivityResolver.NETWORK_MODE, mode, + DefaultConnectivityResolver.CHECK_CREDENTIALS, false)); initNodeCreatorAndJcloudsLocation(newNodeCreator(), ImmutableMap.of( - JcloudsLocationConfig.LOCATION_NETWORK_INFO_CUSTOMIZER, customizer)); + JcloudsLocationConfig.CONNECTIVITY_RESOLVER, customizer)); - ManagementAddressResolveOptions options = newResolveOptions() - .reachableAddressPredicate(Predicates.in(reachableIps)); + ConnectivityResolverOptions options = newResolveOptions() + .pollForReachableAddresses(Predicates.in(reachableIps), Duration.ONE_SECOND, true) + .build(); ConfigBag configBag = ConfigBag.newInstance(); customizer.resolve(jcloudsLocation, newNodeMetadata(), configBag, options); } - private ManagementAddressResolveOptions newResolveOptions() { - return new ManagementAddressResolveOptions() - .expectReachable(true) - .pollForFirstReachableAddress(true) - .pollTimeout(Duration.millis(1)) + private ConnectivityResolverOptions.Builder newResolveOptions() { + return ConnectivityResolverOptions.builder() .initialCredentials(credential); } @@ -241,7 +243,7 @@ private NodeMetadata newNodeMetadata() { .group("group") .hostname("hostname") .loginPort(22) - .name("BasicLocationNetworkInfoCustomizerTest") + .name("DefaultConnectivityResolverTest") .publicAddresses(ImmutableList.of("10.0.0.1", "10.0.0.2")) .privateAddresses(ImmutableList.of("192.168.0.1", "192.168.0.2")) .status(NodeMetadata.Status.RUNNING) diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverStubbedRebindTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverStubbedRebindTest.java index a2bd9dce3fb..0921044d57b 100644 --- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverStubbedRebindTest.java +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverStubbedRebindTest.java @@ -142,8 +142,8 @@ public void testRebind() throws Exception { String spec = "jcloudsByon:(provider=\""+SOFTLAYER_PROVIDER+"\",region=\""+SOFTLAYER_AMS01_REGION_NAME+"\",user=\"myuser\",password=\"mypassword\",hosts=\""+nodeId+"\")"; Map specFlags = ImmutableMap.builder() .put(JcloudsLocationConfig.COMPUTE_SERVICE_REGISTRY, computeServiceRegistry) - .put(JcloudsLocationConfig.WAIT_FOR_SSHABLE, Duration.ONE_MILLISECOND.toString()) - .put(JcloudsLocation.POLL_FOR_FIRST_REACHABLE_ADDRESS, Duration.ONE_MILLISECOND.toString()) + .put(JcloudsLocationConfig.WAIT_FOR_SSHABLE, Duration.ONE_SECOND.toString()) + .put(JcloudsLocation.POLL_FOR_FIRST_REACHABLE_ADDRESS, Duration.ONE_SECOND.toString()) .put(JcloudsLocation.POLL_FOR_FIRST_REACHABLE_ADDRESS_PREDICATE, Predicates.alwaysTrue()) .build(); diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/JcloudsReachableAddressStubbedTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/JcloudsReachableAddressStubbedTest.java index 726d7f3404c..6b2b10a6c90 100644 --- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/JcloudsReachableAddressStubbedTest.java +++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/networking/JcloudsReachableAddressStubbedTest.java @@ -34,8 +34,8 @@ import org.apache.brooklyn.location.jclouds.JcloudsLocationConfig; import org.apache.brooklyn.location.jclouds.JcloudsSshMachineLocation; import org.apache.brooklyn.location.jclouds.JcloudsWinRmMachineLocation; -import org.apache.brooklyn.location.jclouds.BasicLocationNetworkInfoCustomizer; -import org.apache.brooklyn.location.jclouds.LocationNetworkInfoCustomizer; +import org.apache.brooklyn.location.jclouds.DefaultConnectivityResolver; +import org.apache.brooklyn.location.jclouds.ConnectivityResolver; import org.apache.brooklyn.location.jclouds.StubbedComputeServiceRegistry.AbstractNodeCreator; import org.apache.brooklyn.location.jclouds.StubbedComputeServiceRegistry.SingleNodeCreator; import org.apache.brooklyn.location.jclouds.networking.JcloudsPortForwardingStubbedTest.RecordingJcloudsPortForwarderExtension; @@ -238,7 +238,7 @@ public void testNoWaitForSshable() throws Exception { JcloudsSshMachineLocation machine = newMachine(ImmutableMap.,Object>builder() .put(JcloudsLocationConfig.WAIT_FOR_SSHABLE, "false") - .put(JcloudsLocation.POLL_FOR_FIRST_REACHABLE_ADDRESS, Duration.millis(50).toString()) + .put(JcloudsLocation.POLL_FOR_FIRST_REACHABLE_ADDRESS, "false") .build()); addressChooser.assertNotCalled(); @@ -350,35 +350,35 @@ public Object[][] publicPrivateProvider() { List pri = ImmutableList.of("2.1.1.1", "2.1.1.2", "2.1.1.3"); return new Object[][]{ // First available public address chosen. - {pub, pri, BasicLocationNetworkInfoCustomizer.NetworkMode.PREFER_PUBLIC, "1.1.1.1", ImmutableSet.of("1.1.1.2")}, + {pub, pri, DefaultConnectivityResolver.NetworkMode.PREFER_PUBLIC, "1.1.1.1", ImmutableSet.of("1.1.1.2")}, // public desired and reachable. private address ignored. - {pub, pri, BasicLocationNetworkInfoCustomizer.NetworkMode.PREFER_PUBLIC, "1.1.1.2", ImmutableSet.of("2.1.1.1")}, + {pub, pri, DefaultConnectivityResolver.NetworkMode.PREFER_PUBLIC, "1.1.1.2", ImmutableSet.of("2.1.1.1")}, // public desired but unreachable. first reachable private chosen. - {pub, pri, BasicLocationNetworkInfoCustomizer.NetworkMode.PREFER_PUBLIC, "2.1.1.2", ImmutableSet.of("2.1.1.3")}, + {pub, pri, DefaultConnectivityResolver.NetworkMode.PREFER_PUBLIC, "2.1.1.2", ImmutableSet.of("2.1.1.3")}, // private desired and reachable. public addresses ignored. - {pub, pri, BasicLocationNetworkInfoCustomizer.NetworkMode.PREFER_PRIVATE, "2.1.1.2", ImmutableSet.of("1.1.1.1", "1.1.1.2")}, + {pub, pri, DefaultConnectivityResolver.NetworkMode.PREFER_PRIVATE, "2.1.1.2", ImmutableSet.of("1.1.1.1", "1.1.1.2")}, // private desired but unreachable. - {pub, pri, BasicLocationNetworkInfoCustomizer.NetworkMode.PREFER_PRIVATE, "1.1.1.1", ImmutableSet.of()}, + {pub, pri, DefaultConnectivityResolver.NetworkMode.PREFER_PRIVATE, "1.1.1.1", ImmutableSet.of()}, }; } @Test(dataProvider = "publicPrivateProvider") public void testPublicPrivatePreference( List publicAddresses, List privateAddresses, - BasicLocationNetworkInfoCustomizer.NetworkMode networkMode, String preferredIp, ImmutableSet otherReachableIps) throws Exception { + DefaultConnectivityResolver.NetworkMode networkMode, String preferredIp, ImmutableSet otherReachableIps) throws Exception { LOG.info("Checking {} preferred when mode={}, other reachable IPs={}, public addresses={} and private={}", new Object[]{preferredIp, networkMode, otherReachableIps, publicAddresses, privateAddresses}); this.reachableIp = preferredIp; this.additionalReachableIps = otherReachableIps; initNodeCreatorAndJcloudsLocation(newNodeCreator(publicAddresses, privateAddresses), ImmutableMap.of()); - LocationNetworkInfoCustomizer customizer = - new BasicLocationNetworkInfoCustomizer(ImmutableMap.of(BasicLocationNetworkInfoCustomizer.MODE, networkMode)); + ConnectivityResolver customizer = + new DefaultConnectivityResolver(ImmutableMap.of(DefaultConnectivityResolver.NETWORK_MODE, networkMode)); JcloudsSshMachineLocation machine = newMachine(ImmutableMap., Object>builder() .put(JcloudsLocationConfig.WAIT_FOR_SSHABLE, Duration.ONE_SECOND.toString()) .put(JcloudsLocation.POLL_FOR_FIRST_REACHABLE_ADDRESS, Duration.ONE_SECOND.toString()) - .put(JcloudsLocation.LOCATION_NETWORK_INFO_CUSTOMIZER, customizer) + .put(JcloudsLocation.CONNECTIVITY_RESOLVER, customizer) .build()); assertEquals(machine.getAddress().getHostAddress(), preferredIp); diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/AbstractEc2LiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/AbstractEc2LiveTest.java index dd947dbf7ca..e24539513bb 100644 --- a/software/base/src/test/java/org/apache/brooklyn/entity/AbstractEc2LiveTest.java +++ b/software/base/src/test/java/org/apache/brooklyn/entity/AbstractEc2LiveTest.java @@ -18,31 +18,14 @@ */ package org.apache.brooklyn.entity; -import static org.testng.Assert.fail; - -import java.io.ByteArrayOutputStream; -import java.util.List; -import java.util.Map; - import org.apache.brooklyn.api.location.Location; import org.apache.brooklyn.core.internal.BrooklynProperties; -import org.apache.brooklyn.core.location.Machines; -import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport; -import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; -import org.apache.brooklyn.entity.software.base.SoftwareProcess; import org.apache.brooklyn.location.jclouds.JcloudsLocation; import org.apache.brooklyn.location.jclouds.JcloudsLocationConfig; -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.ssh.BashCommands; -import org.apache.brooklyn.util.time.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; /**