This is an automated email from the ASF dual-hosted git repository. heneveld pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git
commit 1006bff329d66683f503f347f248be5ec065dcfb Author: Alex Heneveld <[email protected]> AuthorDate: Fri Jul 19 15:43:40 2024 +0100 byon locations can be dynamic --- .../camp/brooklyn/ByonLocationsYamlTest.java | 35 ++++++++++- .../location/byon/ByonLocationResolver.java | 10 ++-- .../byon/FixedListMachineProvisioningLocation.java | 67 ++++++++++++++++------ 3 files changed, 90 insertions(+), 22 deletions(-) diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java index 6cab0ebb06..e76712b158 100644 --- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java @@ -28,13 +28,16 @@ import java.util.Set; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.api.mgmt.Task; import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.entity.trait.Startable; import org.apache.brooklyn.core.location.LocationPredicates; import org.apache.brooklyn.core.location.Machines; import org.apache.brooklyn.core.location.access.PortForwardManager; import org.apache.brooklyn.core.location.access.PortForwardManagerLocationResolver; import org.apache.brooklyn.core.location.cloud.CloudLocationConfig; +import org.apache.brooklyn.core.sensor.Sensors; import org.apache.brooklyn.entity.software.base.DoNothingSoftwareProcess; import org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation; import org.apache.brooklyn.location.ssh.SshMachineLocation; @@ -42,6 +45,8 @@ import org.apache.brooklyn.location.winrm.WinRmMachineLocation; import org.apache.brooklyn.test.Asserts; import org.apache.brooklyn.util.collections.MutableList; import org.apache.brooklyn.util.net.UserAndHostAndPort; +import org.apache.brooklyn.util.time.Duration; +import org.apache.brooklyn.util.time.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.Test; @@ -121,6 +126,34 @@ public class ByonLocationsYamlTest extends AbstractYamlTest { assertMachine(machine, UserAndHostAndPort.fromParts(machine.getUser(), "1.2.3.4", 22), Collections.emptyMap()); } + @Test + @SuppressWarnings("unchecked") + public void testByonMachineResolvesDynamicDependentConfigForHost() throws Exception { + String yaml = Joiner.on("\n").join( + "location:", + " byon:", + " hosts:", + " - $brooklyn:attributeWhenReady(\"ip_address\")", + "services:", + "- type: org.apache.brooklyn.entity.software.base.DoNothingSoftwareProcess" + ); + + Entity app = createApplicationUnstarted(yaml); + Task<Void> start = app.invoke(Startable.START, null); + Time.sleep(Duration.millis(100)); + Asserts.assertFalse(start.isDone()); + + Iterables.getOnlyElement(app.getChildren()).sensors().set(Sensors.newStringSensor("ip_address"), "1.2.3.4"); + start.getUnchecked(); + start.getUnchecked(Duration.seconds(5)); + + FixedListMachineProvisioningLocation<SshMachineLocation> loc = (FixedListMachineProvisioningLocation<SshMachineLocation>) Iterables.get(app.getLocations(), 0); + + Set<SshMachineLocation> machines = loc.getAllMachines(); + SshMachineLocation machine = Iterables.getOnlyElement(machines); + assertMachine(machine, UserAndHostAndPort.fromParts(machine.getUser(), "1.2.3.4", 22), Collections.emptyMap()); + } + @Test @SuppressWarnings("unchecked") public void testByonMachineResolvesDependentConfigForHosts() throws Exception { @@ -357,7 +390,7 @@ public class ByonLocationsYamlTest extends AbstractYamlTest { Entity app = createStartWaitAndLogApplication(yaml); Asserts.shouldHaveFailedPreviously("app="+app); } catch (Exception e) { - Asserts.expectedFailureContains(e, "Invalid location", "byon", "at least one host must be defined"); + Asserts.expectedFailureContains(e, "Invalid location", "byon", "hosts must be defined"); } } diff --git a/core/src/main/java/org/apache/brooklyn/location/byon/ByonLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/byon/ByonLocationResolver.java index 26d42fd0a6..f219434c32 100644 --- a/core/src/main/java/org/apache/brooklyn/location/byon/ByonLocationResolver.java +++ b/core/src/main/java/org/apache/brooklyn/location/byon/ByonLocationResolver.java @@ -152,9 +152,11 @@ public class ByonLocationResolver extends AbstractLocationResolver { return TypeCoercions.coerce(v, type); } protected <T> T resolveRemoveOrThrow(boolean dynamicAllowed, Map<String, Object> map, String key, Class<T> type) { - Object v = map.remove(key); + Object v = map.get(key); v = resolveOrThrow(dynamicAllowed, v); - return TypeCoercions.coerce(v, type); + T result = TypeCoercions.coerce(v, type); + map.remove(key); + return result; } protected Object resolveOrThrow(boolean dynamicAllowed, Object v) { if (v instanceof DeferredSupplier) { @@ -171,7 +173,6 @@ public class ByonLocationResolver extends AbstractLocationResolver { Class<? extends MachineLocation> locationClass; MutableMap<String, Object> defaultProps = MutableMap.of(); - config.remove("hosts"); String user = resolveOrThrow(dynamicAllowed, "user", String.class); Integer port = resolveOrThrow(dynamicAllowed, "port", Integer.class); resolveOrThrow(dynamicAllowed, OS_FAMILY.getName(), Object.class); @@ -192,7 +193,7 @@ public class ByonLocationResolver extends AbstractLocationResolver { } else if (hosts instanceof Iterable) { hostAddresses = ImmutableList.copyOf((Iterable<?>) hosts); } else { - throw new IllegalArgumentException("Invalid location '" + spec + "'; at least one host must be defined"); + throw new IllegalArgumentException("Invalid location '" + spec + "'; hosts must be defined, as a string of a single host or list of hosts"); } if (hostAddresses.isEmpty()) { throw new IllegalArgumentException("Invalid location '" + spec + "'; at least one host must be defined"); @@ -213,6 +214,7 @@ public class ByonLocationResolver extends AbstractLocationResolver { machineSpec.configureIfNotNull(LocalLocationManager.CREATE_UNMANAGED, config.get(LocalLocationManager.CREATE_UNMANAGED)); machineSpecs.add(machineSpec); } + config.remove("hosts"); return machineSpecs; } diff --git a/core/src/main/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocation.java b/core/src/main/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocation.java index 168bd3078d..3301678716 100644 --- a/core/src/main/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocation.java +++ b/core/src/main/java/org/apache/brooklyn/location/byon/FixedListMachineProvisioningLocation.java @@ -46,6 +46,8 @@ import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.collections.MutableSet; import org.apache.brooklyn.util.core.config.ConfigBag; import org.apache.brooklyn.util.core.flags.SetFromFlag; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.guava.Maybe; import org.apache.brooklyn.util.stream.Streams; import org.apache.brooklyn.util.text.WildcardGlobs; import org.apache.brooklyn.util.text.WildcardGlobs.PhraseTreatment; @@ -158,17 +160,9 @@ implements MachineProvisioningLocation<T>, Closeable { @Override public void init() { super.init(); - - List<LocationSpec<? extends MachineLocation>> machineSpecs = getConfig(MACHINE_SPECS); - if (machineSpecs != null) { - for (LocationSpec<? extends MachineLocation> spec : machineSpecs) { - @SuppressWarnings("unchecked") - T machine = (T) getManagementContext().getLocationManager().createLocation(spec); - machines.add(machine); - } - } - config().set(MACHINE_SPECS, (List<LocationSpec<? extends MachineLocation>>) null); - + + attemptResolveMachineSpecs(false); + Supplier<? extends List<? extends MachineLocation>> initialMachinesFactory = getConfig(INITIAL_MACHINES_FACTORY); if (initialMachinesFactory != null) { List<? extends MachineLocation> initialMachines = initialMachinesFactory.get(); @@ -181,7 +175,11 @@ implements MachineProvisioningLocation<T>, Closeable { } } config().set(INITIAL_MACHINES_FACTORY, (Supplier<List<? extends MachineLocation>>) null); - + + ensureMachinesInitialized(); + } + + private void ensureMachinesInitialized() { Set<T> machinesCopy = MutableSet.of(); for (T location : machines) { if (location==null) { @@ -198,7 +196,34 @@ implements MachineProvisioningLocation<T>, Closeable { machines = machinesCopy; } } - + + transient Exception dynamicMachineSpecsError; + + private void attemptResolveMachineSpecs(boolean ensureMachinesInitialized) { + Maybe<Object> machineSpecsRaw = config().getRaw(MACHINE_SPECS); + if (machineSpecsRaw.isAbsent()) return; + List<LocationSpec<? extends MachineLocation>> machineSpecs = null; + try { + machineSpecs = config().get(MACHINE_SPECS); + } catch (Exception e) { + // don't treat interrupted as severe: + // Exceptions.propagateIfFatal(e); + log.debug("Machine specs for "+this+" are not resolvable at this time. Try again later. ("+e+")"); + dynamicMachineSpecsError = e; + return; + } + dynamicMachineSpecsError = null; + if (machineSpecs != null) { + for (LocationSpec<? extends MachineLocation> spec : machineSpecs) { + @SuppressWarnings("unchecked") + T machine = (T) getManagementContext().getLocationManager().createLocation(spec); + machines.add(machine); + } + } + config().set(MACHINE_SPECS, (List<LocationSpec<? extends MachineLocation>>) null); + if (ensureMachinesInitialized) ensureMachinesInitialized(); + } + @Override public String toVerboseString() { return MoreObjects.toStringHelper(this).omitNullValues() @@ -267,7 +292,7 @@ implements MachineProvisioningLocation<T>, Closeable { } public Set<T> getAvailable() { - Set<T> a = Sets.newLinkedHashSet(machines); + Set<T> a = Sets.newLinkedHashSet(getMachines()); a.removeAll(inUse); return a; } @@ -277,7 +302,7 @@ implements MachineProvisioningLocation<T>, Closeable { } public Set<T> getAllMachines() { - return ImmutableSet.copyOf(machines); + return ImmutableSet.copyOf(getMachines()); } @Override @@ -322,12 +347,20 @@ implements MachineProvisioningLocation<T>, Closeable { synchronized (lock) { Set<T> a = getAvailable(); if (a.isEmpty()) { + if (dynamicMachineSpecsError!=null) { + attemptResolveMachineSpecs(true); + a = getAvailable(); + } if (canProvisionMore()) { provisionMore(1, allflags.getAllConfig()); a = getAvailable(); } - if (a.isEmpty()) - throw new NoMachinesAvailableException("No machines available in "+toString()); + if (a.isEmpty()) { + if (dynamicMachineSpecsError!=null) { + throw new NoMachinesAvailableException("Machines cannot be resolved at this time in " + toString()+": "+dynamicMachineSpecsError, dynamicMachineSpecsError); + } + throw new NoMachinesAvailableException("No machines available in " + toString()); + } } if (desiredMachine != null) { if (a.contains(desiredMachine)) {
