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)) {

Reply via email to