Closes #153
Add Jenkinsfile for CI build
This capture the current Jenkins configuration, so that is repeatable and documented.
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java
new file mode 100644
index 0000000..b79c92d
--- /dev/null
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt.impl;
+
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.entity.cm.salt.SaltConfig;
+import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Supplier;
+
+/**
+ * Kept only for rebinding to historic persisted state; not used.
+ * Not preserving the functionality of any such persisted entities; just ensuring it deserializes.
+ */
+@Beta
+class SaltLifecycleEffectorTasks extends MachineLifecycleEffectorTasks implements SaltConfig {
+
+ @Override
+ protected String startProcessesAtMachine(Supplier<MachineLocation> machineS) {
+ throw new UnsupportedOperationException("Legacy SaltEntity no longer supported");
+ }
+
+ @Override
+ protected String stopProcessesAtMachine() {
+ throw new UnsupportedOperationException("Legacy SaltEntity no longer supported");
+ }
+
+ @SuppressWarnings("unused")
+ private void startWithSshAsync() {
+ new Runnable() {
+ @Override
+ public void run() {
+ throw new UnsupportedOperationException("Legacy SaltEntity no longer supported");
+ }
+ };
+ throw new UnsupportedOperationException("Legacy SaltEntity no longer supported");
+ }
+}
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
index 1c9a37c..ed8b7ee 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
@@ -290,6 +290,7 @@
private final static class ListOfHostAndPort implements Function<Set<Entity>, List<String>> {
@Override public List<String> apply(Set<Entity> input) {
List<String> addresses = Lists.newArrayList();
+ if (input == null) return addresses;
for (Entity entity : input) {
addresses.add(String.format("%s",
BrooklynAccessUtils.getBrooklynAccessibleAddress(entity, entity.getAttribute(CouchbaseNode.COUCHBASE_WEB_ADMIN_PORT))));
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractControllerImpl.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractControllerImpl.java
index 34c324e..bd5bc15 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractControllerImpl.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractControllerImpl.java
@@ -33,6 +33,7 @@
import org.apache.brooklyn.api.policy.Policy;
import org.apache.brooklyn.api.policy.PolicySpec;
import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.annotation.EffectorParam;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.EntityInternal;
@@ -148,9 +149,25 @@
protected void removeServerPoolMemberTrackingPolicy() {
if (serverPoolMemberTrackerPolicy != null) {
policies().remove(serverPoolMemberTrackerPolicy);
+ serverPoolMemberTrackerPolicy = null;
}
}
+ protected boolean hasServerPoolMemberTrackingPolicy() {
+ if (serverPoolMemberTrackerPolicy != null) return true;
+
+ // On rebind, might not have set the field yet
+ for (Policy p: policies()) {
+ if (p instanceof ServerPoolMemberTrackerPolicy) {
+ LOG.info(this+" picking up "+p+" as the tracker (already set, often due to rebind)");
+ serverPoolMemberTrackerPolicy = (ServerPoolMemberTrackerPolicy) p;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public static class ServerPoolMemberTrackerPolicy extends AbstractMembershipTrackingPolicy {
@Override
protected void onEntityEvent(EventType type, Entity entity) {
@@ -411,8 +428,9 @@
public Task<?> updateAsync() {
synchronized (serverPoolAddresses) {
Task<?> result = null;
- if (!isActive()) updateNeeded = true;
- else {
+ if (!isActive()) {
+ updateNeeded = true;
+ } else {
updateNeeded = false;
LOG.debug("Updating {} in response to changes", this);
LOG.info("Updating {}, server pool targets {}", new Object[] {this, getAttribute(SERVER_POOL_TARGETS)});
@@ -425,6 +443,20 @@
}
}
+ @Override
+ public void changeServerPool(String groupId) {
+ Group newGroup = (Group) getManagementContext().getEntityManager().getEntity(groupId);
+ if (newGroup == null) {
+ throw new IllegalArgumentException("Group '"+groupId+"' not found");
+ }
+
+ config().set(SERVER_POOL, newGroup);
+ if (hasServerPoolMemberTrackingPolicy()) {
+ addServerPoolMemberTrackingPolicy();
+ }
+ updateNeeded();
+ }
+
protected void onServerPoolMemberChanged(Entity member) {
synchronized (serverPoolAddresses) {
if (LOG.isTraceEnabled()) LOG.trace("For {}, considering membership of {} which is in locations {}",
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractNonProvisionedControllerImpl.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractNonProvisionedControllerImpl.java
index 7ad7224..24d57fa 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractNonProvisionedControllerImpl.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/AbstractNonProvisionedControllerImpl.java
@@ -39,6 +39,7 @@
import org.apache.brooklyn.core.feed.ConfigToAttributes;
import org.apache.brooklyn.entity.group.AbstractMembershipTrackingPolicy;
import org.apache.brooklyn.entity.proxy.AbstractControllerImpl.MapAttribute;
+import org.apache.brooklyn.entity.proxy.AbstractControllerImpl.ServerPoolMemberTrackerPolicy;
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.slf4j.Logger;
@@ -137,6 +138,9 @@
sensors().set(MAIN_URI, URI.create(inferUrl()));
sensors().set(ROOT_URL, inferUrl());
addServerPoolMemberTrackingPolicy();
+
+ isActive = true;
+ update();
}
protected void preStop() {
@@ -212,9 +216,25 @@
protected void removeServerPoolMemberTrackingPolicy() {
if (serverPoolMemberTrackerPolicy != null) {
policies().remove(serverPoolMemberTrackerPolicy);
+ serverPoolMemberTrackerPolicy = null;
}
}
+ protected boolean hasServerPoolMemberTrackingPolicy() {
+ if (serverPoolMemberTrackerPolicy != null) return true;
+
+ // On rebind, might not have set the field yet
+ for (Policy p: policies()) {
+ if (p instanceof ServerPoolMemberTrackerPolicy) {
+ LOG.info(this+" picking up "+p+" as the tracker (already set, often due to rebind)");
+ serverPoolMemberTrackerPolicy = (ServerPoolMemberTrackerPolicy) p;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public static class ServerPoolMemberTrackerPolicy extends AbstractMembershipTrackingPolicy {
@Override
protected void onEntityEvent(EventType type, Entity entity) {
@@ -260,8 +280,9 @@
public Task<?> updateAsync() {
synchronized (mutex) {
Task<?> result = null;
- if (!isActive()) updateNeeded = true;
- else {
+ if (!isActive()) {
+ updateNeeded = true;
+ } else {
updateNeeded = false;
LOG.debug("Updating {} in response to changes", this);
LOG.info("Updating {}, server pool targets {}", new Object[] {this, getAttribute(SERVER_POOL_TARGETS)});
@@ -273,7 +294,20 @@
}
}
-
+ @Override
+ public void changeServerPool(String groupId) {
+ Group newGroup = (Group) getManagementContext().getEntityManager().getEntity(groupId);
+ if (newGroup == null) {
+ throw new IllegalArgumentException("Group '"+groupId+"' not found");
+ }
+
+ config().set(SERVER_POOL, newGroup);
+ if (hasServerPoolMemberTrackingPolicy()) {
+ addServerPoolMemberTrackingPolicy();
+ }
+ updateNeeded();
+ }
+
protected void onServerPoolMemberChanged(Entity member) {
synchronized (mutex) {
if (LOG.isTraceEnabled()) LOG.trace("For {}, considering membership of {} which is in locations {}",
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/LoadBalancer.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/LoadBalancer.java
index 6ff9cb4..689f22b 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/LoadBalancer.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/LoadBalancer.java
@@ -27,6 +27,7 @@
import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.annotation.Effector;
+import org.apache.brooklyn.core.annotation.EffectorParam;
import org.apache.brooklyn.core.config.BasicConfigKey;
import org.apache.brooklyn.core.effector.MethodEffector;
import org.apache.brooklyn.core.entity.Attributes;
@@ -132,6 +133,10 @@
public static final MethodEffector<Void> UPDATE = new MethodEffector<Void>(LoadBalancer.class, "update");
+ @Effector(description="Change the target server pool")
+ public void changeServerPool(
+ @EffectorParam(name="groupId") String groupId);
+
@Effector(description="Forces reload of the configuration")
public void reload();
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/LoadBalancerClusterImpl.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/LoadBalancerClusterImpl.java
index ff23089..863b7ed 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/LoadBalancerClusterImpl.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/proxy/LoadBalancerClusterImpl.java
@@ -21,6 +21,7 @@
import java.util.Map;
import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.Group;
import org.apache.brooklyn.entity.group.DynamicClusterImpl;
/**
@@ -73,4 +74,18 @@
}
}
}
+
+ @Override
+ public void changeServerPool(String groupId) {
+ Group newGroup = (Group) getManagementContext().getEntityManager().getEntity(groupId);
+ if (newGroup == null) {
+ throw new IllegalArgumentException("Group '"+groupId+"' not found");
+ }
+
+ for (Entity member : getMembers()) {
+ if (member instanceof LoadBalancer) {
+ ((LoadBalancer)member).changeServerPool(groupId);
+ }
+ }
+ }
}
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6Driver.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6Driver.java
index d8ba9d7..b6c07f9 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6Driver.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6Driver.java
@@ -20,5 +20,9 @@
import org.apache.brooklyn.entity.webapp.JavaWebAppDriver;
+/**
+ * @deprecated since 1.0.0; JBoss 6 is EOF
+ */
+@Deprecated
public interface JBoss6Driver extends JavaWebAppDriver {
}
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6Server.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6Server.java
index 73100b2..eb59cfc 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6Server.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6Server.java
@@ -29,6 +29,12 @@
import org.apache.brooklyn.util.core.flags.SetFromFlag;
import org.apache.brooklyn.util.time.Duration;
+import com.google.gson.annotations.Since;
+
+/**
+ * @deprecated since 1.0.0; JBoss 6 is EOF
+ */
+@Deprecated
@Catalog(name="JBoss Application Server 6", description="AS6: an open source Java application server from JBoss", iconUrl="classpath:///jboss-logo.png")
@ImplementedBy(JBoss6ServerImpl.class)
public interface JBoss6Server extends JavaWebAppSoftwareProcess, UsesJmx {
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6ServerImpl.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6ServerImpl.java
index ecbd0c5..6520c09 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6ServerImpl.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6ServerImpl.java
@@ -30,6 +30,10 @@
import com.google.common.base.Functions;
+/**
+ * @deprecated since 1.0.0; JBoss 6 is EOF
+ */
+@Deprecated
public class JBoss6ServerImpl extends JavaWebAppSoftwareProcessImpl implements JBoss6Server {
public static final Logger log = LoggerFactory.getLogger(JBoss6ServerImpl.class);
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6SshDriver.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6SshDriver.java
index 1ed0e64..0efc517 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6SshDriver.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss6SshDriver.java
@@ -39,6 +39,10 @@
import org.apache.brooklyn.util.ssh.BashCommands;
import org.apache.brooklyn.util.time.Duration;
+/**
+ * @deprecated since 1.0.0; JBoss 6 is EOF
+ */
+@Deprecated
public class JBoss6SshDriver extends JavaWebAppSshDriver implements JBoss6Driver {
public static final String SERVER_TYPE = "standard";
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7Driver.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7Driver.java
index ad5e101..2bd858d 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7Driver.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7Driver.java
@@ -20,6 +20,10 @@
import org.apache.brooklyn.entity.webapp.JavaWebAppDriver;
+/**
+ * @deprecated since 1.0.0; JBoss 7 is EOF
+ */
+@Deprecated
public interface JBoss7Driver extends JavaWebAppDriver{
/**
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7Server.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7Server.java
index 7cb3e1b..af84504 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7Server.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7Server.java
@@ -33,6 +33,10 @@
import org.apache.brooklyn.util.core.flags.SetFromFlag;
import org.apache.brooklyn.util.javalang.JavaClassNames;
+/**
+ * @deprecated since 1.0.0; JBoss 7 is EOF
+ */
+@Deprecated
@Catalog(name="JBoss Application Server 7", description="AS7: an open source Java application server from JBoss", iconUrl="classpath:///jboss-logo.png")
@ImplementedBy(JBoss7ServerImpl.class)
public interface JBoss7Server extends JavaWebAppSoftwareProcess, HasShortName {
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7ServerImpl.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7ServerImpl.java
index 198cafa..b1dfba8 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7ServerImpl.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7ServerImpl.java
@@ -37,6 +37,10 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.net.HostAndPort;
+/**
+ * @deprecated since 1.0.0; JBoss 7 is EOF
+ */
+@Deprecated
public class JBoss7ServerImpl extends JavaWebAppSoftwareProcessImpl implements JBoss7Server {
public static final Logger log = LoggerFactory.getLogger(JBoss7ServerImpl.class);
diff --git a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7SshDriver.java b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7SshDriver.java
index 081cd11..7a079d7 100644
--- a/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7SshDriver.java
+++ b/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/jboss/JBoss7SshDriver.java
@@ -45,6 +45,10 @@
import com.google.common.hash.Hashing;
import com.google.common.io.BaseEncoding;
+/**
+ * @deprecated since 1.0.0; JBoss 7 is EOF
+ */
+@Deprecated
public class JBoss7SshDriver extends JavaWebAppSshDriver implements JBoss7Driver {
private static final Logger LOG = LoggerFactory.getLogger(JBoss7SshDriver.class);
@@ -123,15 +127,31 @@
List<String> urls = resolver.getTargets();
String saveAs = resolver.getFilename();
- List<String> commands = new LinkedList<String>();
- commands.addAll(BashCommands.commandsToDownloadUrlsAs(urls, saveAs));
- commands.add(BashCommands.INSTALL_TAR);
- commands.add("tar xzfv " + saveAs);
+ List<String> installCommands = new LinkedList<String>();
+ installCommands.addAll(BashCommands.commandsToDownloadUrlsAs(urls, saveAs));
+ installCommands.add(BashCommands.INSTALL_TAR);
+ installCommands.add("tar xzfv " + saveAs);
newScript(INSTALLING)
// don't set vars yet -- it resolves dependencies (e.g. DB) which we don't want until we start
.environmentVariablesReset()
- .body.append(commands)
+ .body.append(installCommands)
+ .execute();
+
+ // The jboss-modules.jar that comes with jBoss 7.1.1 is of version 1.1.1.GA. However, this has issue on initialisation.
+ // Hopefully, version 1.1.5.GA fixes the init issue. However, as jBoss 7.1.1 is EOF, we need to download and replace
+ // this jar before starting up the server
+ // see: https://stackoverflow.com/questions/48403832/javax-xml-parsers-factoryconfigurationerror-running-jboss-as-7-1-with-java-7-upd
+ String installDir = getExpandedInstallDir();
+ List<String> fixBugCommands = new LinkedList<String>();
+ fixBugCommands.add(BashCommands.INSTALL_WGET);
+ fixBugCommands.add(format("rm %s/jboss-modules.jar", installDir));
+ fixBugCommands.add(format("wget http://repo1.maven.org/maven2/org/jboss/modules/jboss-modules/1.1.5.GA/jboss-modules-1.1.5.GA.jar -O %s/jboss-modules.jar", installDir));
+
+ newScript("fix JBoss 7.1.1 init bug")
+ // don't set vars yet -- it resolves dependencies (e.g. DB) which we don't want until we start
+ .environmentVariablesReset()
+ .body.append(fixBugCommands)
.execute();
}
diff --git a/software/webapp/src/main/resources/catalog.bom b/software/webapp/src/main/resources/catalog.bom
index 38ab15e..9d99854 100644
--- a/software/webapp/src/main/resources/catalog.bom
+++ b/software/webapp/src/main/resources/catalog.bom
@@ -28,8 +28,9 @@
iconUrl: classpath:///jboss_logo.png
item:
type: org.apache.brooklyn.entity.webapp.jboss.JBoss7Server
- name: JBoss Application Server 7
+ name: "[DEPRECATED] JBoss Application Server 7"
description: AS7 - an open source Java application server from JBoss
+ deprecated: true
- id: org.apache.brooklyn.entity.proxy.nginx.UrlMapping
item:
type: org.apache.brooklyn.entity.proxy.nginx.UrlMapping
@@ -48,8 +49,9 @@
iconUrl: classpath:///jboss_logo.png
item:
type: org.apache.brooklyn.entity.webapp.jboss.JBoss6Server
- name: JBoss Application Server 6
+ name: "[DEPRECATED] JBoss Application Server 6"
description: AS6 - an open source Java application server from JBoss
+ deprecated: true
- id: org.apache.brooklyn.entity.webapp.tomcat.Tomcat8Server
iconUrl: classpath:///tomcat-logo.png
item:
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractAbstractControllerTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractAbstractControllerTest.java
new file mode 100644
index 0000000..a9065c5
--- /dev/null
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractAbstractControllerTest.java
@@ -0,0 +1,340 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.proxy;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.net.Inet4Address;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.location.HasSubnetHostname;
+import org.apache.brooklyn.core.location.Machines;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.core.test.entity.TestEntityImpl;
+import org.apache.brooklyn.entity.group.Cluster;
+import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation;
+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.collections.MutableSet;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
+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.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+/**
+ * Sub-classes are concrete tests of {@link AbstractController} and {@link AbstractNonProvisionedController},
+ * hence the weird double-abstract name!
+ */
+public abstract class AbstractAbstractControllerTest<T extends LoadBalancer> extends BrooklynAppUnitTestSupport {
+
+ private static final Logger log = LoggerFactory.getLogger(AbstractAbstractControllerTest.class);
+
+ protected FixedListMachineProvisioningLocation<?> loc;
+ protected Cluster cluster;
+ protected T controller;
+
+ @BeforeMethod(alwaysRun = true)
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ List<SshMachineLocation> machines = new ArrayList<SshMachineLocation>();
+ for (int i = 1; i <= 10; i++) {
+ SshMachineLocation machine = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+ .configure("address", Inet4Address.getByName("1.1.1."+i)));
+ machines.add(machine);
+ }
+ loc = mgmt.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
+ .configure("machines", machines));
+
+ cluster = app.addChild(EntitySpec.create(DynamicCluster.class)
+ .configure("initialSize", 0)
+ .configure("memberSpec", EntitySpec.create(TestEntity.class).impl(WebServerEntity.class)));
+
+ controller = newController();
+
+ app.start(ImmutableList.of(loc));
+ }
+
+ /**
+ * Called during {@link #setUp()}, after app and cluster are created (but before app.start is called).
+ */
+ protected abstract T newController();
+
+ protected abstract List<Collection<String>> getUpdates(T controller);
+
+ // Fixes bug where entity that wrapped an AS7 entity was never added to nginx because hostname+port
+ // was set after service_up. Now we listen to those changes and reset the nginx pool when these
+ // values change.
+ @Test
+ public void testUpdateCalledWhenChildHostnameAndPortChanges() throws Exception {
+ log.info("adding child (no effect until up)");
+ TestEntity child = cluster.addChild(EntitySpec.create(TestEntity.class));
+ cluster.addMember(child);
+
+ List<Collection<String>> u = Lists.newArrayList(getUpdates(controller));
+ assertTrue(u.isEmpty(), "expected no updates, but got "+u);
+
+ log.info("setting child service_up");
+ child.sensors().set(Startable.SERVICE_UP, true);
+ // above may trigger error logged about no hostname, but should update again with the settings below
+
+ log.info("setting mymachine:1234");
+ child.sensors().set(WebServerEntity.HOSTNAME, "mymachine");
+ child.sensors().set(Attributes.SUBNET_HOSTNAME, "mymachine");
+ child.sensors().set(WebServerEntity.HTTP_PORT, 1234);
+ assertEventuallyExplicitAddressesMatch(ImmutableList.of("mymachine:1234"));
+
+ /* a race failure has been observed, https://issues.apache.org/jira/browse/BROOKLYN-206
+ * but now (two months later) i (alex) can't see how it could happen.
+ * probably optimistic but maybe it is fixed. if not we'll need the debug logs to see what is happening.
+ * i've confirmed:
+ * * the policy is attached and active during setup, before start completes
+ * * the child is added as a member synchronously
+ * * the policy which is "subscribed to members" is in fact subscribed to everything
+ * then filtered for members, not ideal, but there shouldn't be a race in the policy getting notices
+ * * the handling of those events are both processed in order and look up the current values
+ * rather than relying on the published values; either should be sufficient to cause the addresses to change
+ * there was a sleep(100) marked "Ugly sleep to allow AbstractController to detect node having been added"
+ * from the test's addition by aled in early 2014, but can't see why that would be necessary
+ */
+
+ log.info("setting mymachine2:1234");
+ child.sensors().set(WebServerEntity.HOSTNAME, "mymachine2");
+ child.sensors().set(Attributes.SUBNET_HOSTNAME, "mymachine2");
+ assertEventuallyExplicitAddressesMatch(ImmutableList.of("mymachine2:1234"));
+
+ log.info("setting mymachine2:1235");
+ child.sensors().set(WebServerEntity.HTTP_PORT, 1235);
+ assertEventuallyExplicitAddressesMatch(ImmutableList.of("mymachine2:1235"));
+
+ log.info("clearing");
+ child.sensors().set(WebServerEntity.HOSTNAME, null);
+ child.sensors().set(Attributes.SUBNET_HOSTNAME, null);
+ assertEventuallyExplicitAddressesMatch(ImmutableList.<String>of());
+ }
+
+ @Test
+ public void testUpdateCalledWithAddressesOfNewChildren() {
+ // First child
+ cluster.resize(1);
+ Entity child = Iterables.getOnlyElement(cluster.getMembers());
+
+ List<Collection<String>> u = Lists.newArrayList(getUpdates(controller));
+ assertTrue(u.isEmpty(), "expected empty list but got "+u);
+
+ child.sensors().set(WebServerEntity.HTTP_PORT, 1234);
+ child.sensors().set(Startable.SERVICE_UP, true);
+ assertEventuallyAddressesMatchCluster();
+
+ // Second child
+ cluster.resize(2);
+ Asserts.succeedsEventually(new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(cluster.getMembers().size(), 2);
+ }});
+ Entity child2 = Iterables.getOnlyElement(MutableSet.<Entity>builder().addAll(cluster.getMembers()).remove(child).build());
+
+ child2.sensors().set(WebServerEntity.HTTP_PORT, 1234);
+ child2.sensors().set(Startable.SERVICE_UP, true);
+ assertEventuallyAddressesMatchCluster();
+
+ // And remove all children; expect all addresses to go away
+ cluster.resize(0);
+ assertEventuallyAddressesMatchCluster();
+ }
+
+ @Test(groups = "Integration", invocationCount=10)
+ public void testUpdateCalledWithAddressesOfNewChildrenManyTimes() {
+ testUpdateCalledWithAddressesOfNewChildren();
+ }
+
+ @Test
+ public void testUpdateCalledWithAddressesRemovedForStoppedChildren() {
+ // Get some children, so we can remove one...
+ cluster.resize(2);
+ for (Entity it: cluster.getMembers()) {
+ it.sensors().set(WebServerEntity.HTTP_PORT, 1234);
+ it.sensors().set(Startable.SERVICE_UP, true);
+ }
+ assertEventuallyAddressesMatchCluster();
+
+ // Now remove one child
+ cluster.resize(1);
+ assertEquals(cluster.getMembers().size(), 1);
+ assertEventuallyAddressesMatchCluster();
+ }
+
+ @Test
+ public void testUpdateCalledWithAddressesRemovedForServiceDownChildrenThatHaveClearedHostnamePort() {
+ // Get some children, so we can remove one...
+ cluster.resize(2);
+ for (Entity it: cluster.getMembers()) {
+ it.sensors().set(WebServerEntity.HTTP_PORT, 1234);
+ it.sensors().set(Startable.SERVICE_UP, true);
+ }
+ assertEventuallyAddressesMatchCluster();
+
+ // Now unset host/port, and remove children
+ // Note the unsetting of hostname is done in SoftwareProcessImpl.stop(), so this is realistic
+ for (Entity it : cluster.getMembers()) {
+ it.sensors().set(WebServerEntity.HTTP_PORT, null);
+ it.sensors().set(WebServerEntity.HOSTNAME, null);
+ it.sensors().set(Startable.SERVICE_UP, false);
+ }
+ assertEventuallyAddressesMatch(ImmutableList.<Entity>of());
+ }
+
+ @Test
+ public void testUpdateCalledWhenServerPoolGroupSwapped() {
+ // Second cluster with one child
+ DynamicCluster cluster2 = app.addChild(EntitySpec.create(DynamicCluster.class)
+ .configure("initialSize", 1)
+ .configure("memberSpec", EntitySpec.create(TestEntity.class).impl(WebServerEntity.class)));
+ cluster2.start(ImmutableList.of());
+
+ Entity child = Iterables.getOnlyElement(cluster2.getMembers());
+ child.sensors().set(WebServerEntity.HTTP_PORT, 1234);
+ child.sensors().set(Startable.SERVICE_UP, true);
+
+ // Reconfigure the controller to point at the new cluster
+ controller.changeServerPool(cluster2.getId());
+ assertEquals(controller.config().get(LoadBalancer.SERVER_POOL), cluster2);
+ assertEventuallyAddressesMatchCluster(cluster2);
+
+ // And remove all children; expect all addresses to go away
+ cluster2.resize(0);
+ assertEventuallyAddressesMatchCluster(cluster2);
+ }
+
+ protected void assertEventuallyAddressesMatchCluster() {
+ assertEventuallyAddressesMatchCluster(cluster);
+ }
+
+ protected void assertEventuallyAddressesMatchCluster(Cluster cluster) {
+ assertEventuallyAddressesMatch(cluster.getMembers());
+ }
+
+ protected void assertEventuallyAddressesMatch(final Collection<Entity> expectedMembers) {
+ Asserts.succeedsEventually(new Runnable() {
+ @Override public void run() {
+ assertAddressesMatch(locationsToAddresses(1234, expectedMembers));
+ }} );
+ }
+
+ protected void assertEventuallyExplicitAddressesMatch(final Collection<String> expectedAddresses) {
+ Asserts.succeedsEventually(new Runnable() {
+ @Override public void run() {
+ assertAddressesMatch(expectedAddresses);
+ }} );
+ }
+
+ protected void assertAddressesMatch(final Collection<String> expectedAddresses) {
+ List<Collection<String>> u = Lists.newArrayList(getUpdates(controller));
+ Collection<String> last = Iterables.getLast(u, null);
+ log.debug("test "+u.size()+" updates, expecting "+expectedAddresses+"; actual "+last);
+ assertTrue(!u.isEmpty(), "no updates; expecting "+expectedAddresses);
+ assertEquals(ImmutableSet.copyOf(last), ImmutableSet.copyOf(expectedAddresses), "actual="+last+" expected="+expectedAddresses);
+ assertEquals(last.size(), expectedAddresses.size(), "actual="+last+" expected="+expectedAddresses);
+ }
+
+ protected Collection<String> locationsToAddresses(int port, Collection<Entity> entities) {
+ Set<String> result = MutableSet.of();
+ for (Entity e : entities) {
+ SshMachineLocation machine = Machines.findUniqueMachineLocation(e.getLocations(), SshMachineLocation.class).get();
+ result.add(machine.getAddress().getHostName()+":"+port);
+ }
+ return result;
+ }
+
+ public static class SshMachineLocationWithSubnetHostname extends SshMachineLocation implements HasSubnetHostname {
+ @Override public String getSubnetHostname() {
+ return getSubnetIp();
+ }
+ @Override public String getSubnetIp() {
+ Set<String> addrs = getPrivateAddresses();
+ return (addrs.isEmpty()) ? getAddress().getHostAddress() : Iterables.get(addrs, 0);
+ }
+ }
+
+ public static class WebServerEntity extends TestEntityImpl {
+ @SetFromFlag("hostname")
+ public static final AttributeSensor<String> HOSTNAME = Attributes.HOSTNAME;
+
+ @SetFromFlag("port")
+ public static final AttributeSensor<Integer> HTTP_PORT = Attributes.HTTP_PORT;
+
+ @SetFromFlag("hostAndPort")
+ public static final AttributeSensor<String> HOST_AND_PORT = Attributes.HOST_AND_PORT;
+
+ MachineProvisioningLocation<MachineLocation> provisioner;
+
+ @Override
+ public void start(Collection<? extends Location> locs) {
+ provisioner = (MachineProvisioningLocation<MachineLocation>) locs.iterator().next();
+ MachineLocation machine;
+ try {
+ machine = provisioner.obtain(MutableMap.of());
+ } catch (NoMachinesAvailableException e) {
+ throw Exceptions.propagate(e);
+ }
+ addLocations(Arrays.asList(machine));
+ sensors().set(HOSTNAME, machine.getAddress().getHostName());
+ sensors().set(Attributes.SUBNET_HOSTNAME, machine.getAddress().getHostName());
+ sensors().set(Attributes.MAIN_URI_MAPPED_SUBNET, URI.create(machine.getAddress().getHostName()));
+ sensors().set(Attributes.MAIN_URI_MAPPED_PUBLIC, URI.create("http://8.8.8.8:" + sensors().get(HTTP_PORT)));
+ }
+
+ @Override
+ public void stop() {
+ Maybe<MachineLocation> machine = Machines.findUniqueMachineLocation(getLocations(), MachineLocation.class);
+ if (provisioner != null) {
+ provisioner.release(machine.get());
+ }
+ }
+ }
+}
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractControllerTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractControllerTest.java
index ea4b4d8..25627d8 100644
--- a/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractControllerTest.java
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractControllerTest.java
@@ -18,210 +18,46 @@
*/
package org.apache.brooklyn.entity.proxy;
-import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.net.Inet4Address;
import java.net.URI;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.api.location.LocationSpec;
-import org.apache.brooklyn.api.location.MachineLocation;
-import org.apache.brooklyn.api.location.MachineProvisioningLocation;
-import org.apache.brooklyn.api.location.NoMachinesAvailableException;
-import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.entity.EntityAsserts;
import org.apache.brooklyn.core.entity.trait.Startable;
-import org.apache.brooklyn.core.location.HasSubnetHostname;
-import org.apache.brooklyn.core.location.Machines;
import org.apache.brooklyn.core.location.PortRanges;
-import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
import org.apache.brooklyn.core.test.entity.TestEntity;
-import org.apache.brooklyn.core.test.entity.TestEntityImpl;
-import org.apache.brooklyn.entity.group.Cluster;
-import org.apache.brooklyn.entity.group.DynamicCluster;
-import org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation;
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.collections.MutableSet;
-import org.apache.brooklyn.util.core.flags.SetFromFlag;
import org.apache.brooklyn.util.exceptions.Exceptions;
-import org.apache.brooklyn.util.guava.Maybe;
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.ImmutableSet;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
-public class AbstractControllerTest extends BrooklynAppUnitTestSupport {
+public class AbstractControllerTest extends AbstractAbstractControllerTest<TrackingAbstractController> {
private static final Logger log = LoggerFactory.getLogger(AbstractControllerTest.class);
- FixedListMachineProvisioningLocation<?> loc;
- Cluster cluster;
- TrackingAbstractController controller;
-
- @BeforeMethod(alwaysRun = true)
@Override
- public void setUp() throws Exception {
- super.setUp();
-
- List<SshMachineLocation> machines = new ArrayList<SshMachineLocation>();
- for (int i = 1; i <= 10; i++) {
- SshMachineLocation machine = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
- .configure("address", Inet4Address.getByName("1.1.1."+i)));
- machines.add(machine);
- }
- loc = mgmt.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
- .configure("machines", machines));
-
- cluster = app.addChild(EntitySpec.create(DynamicCluster.class)
- .configure("initialSize", 0)
- .configure("memberSpec", EntitySpec.create(TestEntity.class).impl(WebServerEntity.class)));
-
- controller = app.addChild(EntitySpec.create(TrackingAbstractController.class)
+ protected TrackingAbstractController newController() {
+ return app.addChild(EntitySpec.create(TrackingAbstractController.class)
.configure("serverPool", cluster)
.configure("portNumberSensor", WebServerEntity.HTTP_PORT)
.configure("domain", "mydomain"));
-
- app.start(ImmutableList.of(loc));
}
- // Fixes bug where entity that wrapped an AS7 entity was never added to nginx because hostname+port
- // was set after service_up. Now we listen to those changes and reset the nginx pool when these
- // values change.
- @Test
- public void testUpdateCalledWhenChildHostnameAndPortChanges() throws Exception {
- log.info("adding child (no effect until up)");
- TestEntity child = cluster.addChild(EntitySpec.create(TestEntity.class));
- cluster.addMember(child);
-
- List<Collection<String>> u = Lists.newArrayList(controller.getUpdates());
- assertTrue(u.isEmpty(), "expected no updates, but got "+u);
-
- log.info("setting child service_up");
- child.sensors().set(Startable.SERVICE_UP, true);
- // above may trigger error logged about no hostname, but should update again with the settings below
-
- log.info("setting mymachine:1234");
- child.sensors().set(WebServerEntity.HOSTNAME, "mymachine");
- child.sensors().set(Attributes.SUBNET_HOSTNAME, "mymachine");
- child.sensors().set(WebServerEntity.HTTP_PORT, 1234);
- assertEventuallyExplicitAddressesMatch(ImmutableList.of("mymachine:1234"));
-
- /* a race failure has been observed, https://issues.apache.org/jira/browse/BROOKLYN-206
- * but now (two months later) i (alex) can't see how it could happen.
- * probably optimistic but maybe it is fixed. if not we'll need the debug logs to see what is happening.
- * i've confirmed:
- * * the policy is attached and active during setup, before start completes
- * * the child is added as a member synchronously
- * * the policy which is "subscribed to members" is in fact subscribed to everything
- * then filtered for members, not ideal, but there shouldn't be a race in the policy getting notices
- * * the handling of those events are both processed in order and look up the current values
- * rather than relying on the published values; either should be sufficient to cause the addresses to change
- * there was a sleep(100) marked "Ugly sleep to allow AbstractController to detect node having been added"
- * from the test's addition by aled in early 2014, but can't see why that would be necessary
- */
-
- log.info("setting mymachine2:1234");
- child.sensors().set(WebServerEntity.HOSTNAME, "mymachine2");
- child.sensors().set(Attributes.SUBNET_HOSTNAME, "mymachine2");
- assertEventuallyExplicitAddressesMatch(ImmutableList.of("mymachine2:1234"));
-
- log.info("setting mymachine2:1235");
- child.sensors().set(WebServerEntity.HTTP_PORT, 1235);
- assertEventuallyExplicitAddressesMatch(ImmutableList.of("mymachine2:1235"));
-
- log.info("clearing");
- child.sensors().set(WebServerEntity.HOSTNAME, null);
- child.sensors().set(Attributes.SUBNET_HOSTNAME, null);
- assertEventuallyExplicitAddressesMatch(ImmutableList.<String>of());
- }
-
- @Test
- public void testUpdateCalledWithAddressesOfNewChildren() {
- // First child
- cluster.resize(1);
- Entity child = Iterables.getOnlyElement(cluster.getMembers());
-
- List<Collection<String>> u = Lists.newArrayList(controller.getUpdates());
- assertTrue(u.isEmpty(), "expected empty list but got "+u);
-
- child.sensors().set(WebServerEntity.HTTP_PORT, 1234);
- child.sensors().set(Startable.SERVICE_UP, true);
- assertEventuallyAddressesMatchCluster();
-
- // Second child
- cluster.resize(2);
- Asserts.succeedsEventually(new Runnable() {
- @Override
- public void run() {
- assertEquals(cluster.getMembers().size(), 2);
- }});
- Entity child2 = Iterables.getOnlyElement(MutableSet.<Entity>builder().addAll(cluster.getMembers()).remove(child).build());
-
- child2.sensors().set(WebServerEntity.HTTP_PORT, 1234);
- child2.sensors().set(Startable.SERVICE_UP, true);
- assertEventuallyAddressesMatchCluster();
-
- // And remove all children; expect all addresses to go away
- cluster.resize(0);
- assertEventuallyAddressesMatchCluster();
- }
-
- @Test(groups = "Integration", invocationCount=10)
- public void testUpdateCalledWithAddressesOfNewChildrenManyTimes() {
- testUpdateCalledWithAddressesOfNewChildren();
- }
-
- @Test
- public void testUpdateCalledWithAddressesRemovedForStoppedChildren() {
- // Get some children, so we can remove one...
- cluster.resize(2);
- for (Entity it: cluster.getMembers()) {
- it.sensors().set(WebServerEntity.HTTP_PORT, 1234);
- it.sensors().set(Startable.SERVICE_UP, true);
- }
- assertEventuallyAddressesMatchCluster();
-
- // Now remove one child
- cluster.resize(1);
- assertEquals(cluster.getMembers().size(), 1);
- assertEventuallyAddressesMatchCluster();
- }
-
- @Test
- public void testUpdateCalledWithAddressesRemovedForServiceDownChildrenThatHaveClearedHostnamePort() {
- // Get some children, so we can remove one...
- cluster.resize(2);
- for (Entity it: cluster.getMembers()) {
- it.sensors().set(WebServerEntity.HTTP_PORT, 1234);
- it.sensors().set(Startable.SERVICE_UP, true);
- }
- assertEventuallyAddressesMatchCluster();
-
- // Now unset host/port, and remove children
- // Note the unsetting of hostname is done in SoftwareProcessImpl.stop(), so this is realistic
- for (Entity it : cluster.getMembers()) {
- it.sensors().set(WebServerEntity.HTTP_PORT, null);
- it.sensors().set(WebServerEntity.HOSTNAME, null);
- it.sensors().set(Startable.SERVICE_UP, false);
- }
- assertEventuallyAddressesMatch(ImmutableList.<Entity>of());
+ @Override
+ protected List<Collection<String>> getUpdates(TrackingAbstractController controller) {
+ return controller.getUpdates();
}
@Test
@@ -333,86 +169,4 @@
EntityAsserts.assertAttributeEquals(controller2, Attributes.MAIN_URI_MAPPED_PUBLIC, URI.create("http://1.1.1.1:8081/"));
EntityAsserts.assertAttributeEquals(controller2, Attributes.MAIN_URI_MAPPED_SUBNET, URI.create("http://2.2.2.2:8081/"));
}
- public static class SshMachineLocationWithSubnetHostname extends SshMachineLocation implements HasSubnetHostname {
- @Override public String getSubnetHostname() {
- return getSubnetIp();
- }
- @Override public String getSubnetIp() {
- Set<String> addrs = getPrivateAddresses();
- return (addrs.isEmpty()) ? getAddress().getHostAddress() : Iterables.get(addrs, 0);
- }
- }
-
- private void assertEventuallyAddressesMatchCluster() {
- assertEventuallyAddressesMatch(cluster.getMembers());
- }
-
- private void assertEventuallyAddressesMatch(final Collection<Entity> expectedMembers) {
- Asserts.succeedsEventually(new Runnable() {
- @Override public void run() {
- assertAddressesMatch(locationsToAddresses(1234, expectedMembers));
- }} );
- }
-
- private void assertEventuallyExplicitAddressesMatch(final Collection<String> expectedAddresses) {
- Asserts.succeedsEventually(new Runnable() {
- @Override public void run() {
- assertAddressesMatch(expectedAddresses);
- }} );
- }
-
- private void assertAddressesMatch(final Collection<String> expectedAddresses) {
- List<Collection<String>> u = Lists.newArrayList(controller.getUpdates());
- Collection<String> last = Iterables.getLast(u, null);
- log.debug("test "+u.size()+" updates, expecting "+expectedAddresses+"; actual "+last);
- assertTrue(!u.isEmpty(), "no updates; expecting "+expectedAddresses);
- assertEquals(ImmutableSet.copyOf(last), ImmutableSet.copyOf(expectedAddresses), "actual="+last+" expected="+expectedAddresses);
- assertEquals(last.size(), expectedAddresses.size(), "actual="+last+" expected="+expectedAddresses);
- }
-
- private Collection<String> locationsToAddresses(int port, Collection<Entity> entities) {
- Set<String> result = MutableSet.of();
- for (Entity e : entities) {
- SshMachineLocation machine = Machines.findUniqueMachineLocation(e.getLocations(), SshMachineLocation.class).get();
- result.add(machine.getAddress().getHostName()+":"+port);
- }
- return result;
- }
-
- public static class WebServerEntity extends TestEntityImpl {
- @SetFromFlag("hostname")
- public static final AttributeSensor<String> HOSTNAME = Attributes.HOSTNAME;
-
- @SetFromFlag("port")
- public static final AttributeSensor<Integer> HTTP_PORT = Attributes.HTTP_PORT;
-
- @SetFromFlag("hostAndPort")
- public static final AttributeSensor<String> HOST_AND_PORT = Attributes.HOST_AND_PORT;
-
- MachineProvisioningLocation<MachineLocation> provisioner;
-
- @Override
- public void start(Collection<? extends Location> locs) {
- provisioner = (MachineProvisioningLocation<MachineLocation>) locs.iterator().next();
- MachineLocation machine;
- try {
- machine = provisioner.obtain(MutableMap.of());
- } catch (NoMachinesAvailableException e) {
- throw Exceptions.propagate(e);
- }
- addLocations(Arrays.asList(machine));
- sensors().set(HOSTNAME, machine.getAddress().getHostName());
- sensors().set(Attributes.SUBNET_HOSTNAME, machine.getAddress().getHostName());
- sensors().set(Attributes.MAIN_URI_MAPPED_SUBNET, URI.create(machine.getAddress().getHostName()));
- sensors().set(Attributes.MAIN_URI_MAPPED_PUBLIC, URI.create("http://8.8.8.8:" + sensors().get(HTTP_PORT)));
- }
-
- @Override
- public void stop() {
- Maybe<MachineLocation> machine = Machines.findUniqueMachineLocation(getLocations(), MachineLocation.class);
- if (provisioner != null) {
- provisioner.release(machine.get());
- }
- }
- }
}
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractNonProvisionedControllerTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractNonProvisionedControllerTest.java
new file mode 100644
index 0000000..88094f4
--- /dev/null
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/AbstractNonProvisionedControllerTest.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.proxy;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AbstractNonProvisionedControllerTest extends AbstractAbstractControllerTest<TrackingAbstractNonProvisionedController> {
+
+ // TODO Duplication of AbstractControllerTest
+
+ private static final Logger log = LoggerFactory.getLogger(AbstractNonProvisionedControllerTest.class);
+
+ @Override
+ protected TrackingAbstractNonProvisionedController newController() {
+ return app.addChild(EntitySpec.create(TrackingAbstractNonProvisionedController.class)
+ .configure("serverPool", cluster)
+ .configure("portNumberSensor", WebServerEntity.HTTP_PORT)
+ .configure("domain", "mydomain"));
+ }
+
+ @Override
+ protected List<Collection<String>> getUpdates(TrackingAbstractNonProvisionedController controller) {
+ return controller.getUpdates();
+ }
+}
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/TrackingAbstractNonProvisionedController.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/TrackingAbstractNonProvisionedController.java
new file mode 100644
index 0000000..f0c6606
--- /dev/null
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/TrackingAbstractNonProvisionedController.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.proxy;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.ImplementedBy;
+
+@ImplementedBy(TrackingAbstractNonProvisionedControllerImpl.class)
+public interface TrackingAbstractNonProvisionedController extends AbstractNonProvisionedController {
+ List<Collection<String>> getUpdates();
+}
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/TrackingAbstractNonProvisionedControllerImpl.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/TrackingAbstractNonProvisionedControllerImpl.java
new file mode 100644
index 0000000..896e5d6
--- /dev/null
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/proxy/TrackingAbstractNonProvisionedControllerImpl.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.proxy;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.util.text.Identifiers;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Lists;
+
+public class TrackingAbstractNonProvisionedControllerImpl extends AbstractNonProvisionedControllerImpl implements TrackingAbstractNonProvisionedController {
+
+ private static final Logger log = LoggerFactory.getLogger(TrackingAbstractNonProvisionedControllerImpl.class);
+
+ private final List<Collection<String>> updates = Lists.newCopyOnWriteArrayList();
+
+ @Override
+ public void start(Collection<? extends Location> locations) {
+ sensors().set(HOSTNAME, Identifiers.makeRandomId(8) + ".test.brooklyn.apache.org");
+ super.start(locations);
+ }
+
+ @Override
+ public List<Collection<String>> getUpdates() {
+ return updates;
+ }
+
+ @Override
+ protected void reconfigureService() {
+ Set<String> addresses = getServerPoolAddresses();
+ log.info("test controller reconfigure, targets "+addresses);
+ if ((!addresses.isEmpty() && updates.isEmpty()) || (!updates.isEmpty() && addresses != updates.get(updates.size()-1))) {
+ updates.add(addresses);
+ }
+ }
+
+ @Override
+ public void reload() {
+ // no-op
+ }
+
+ @Override
+ public void restart() {
+ // no-op
+ }
+
+ @Override
+ protected String inferProtocol() {
+ String result = config().get(PROTOCOL);
+ return (result == null ? result : result.toLowerCase());
+ }
+
+ @Override
+ protected String inferUrl() {
+ String scheme = inferProtocol();
+ Integer port = sensors().get(PROXY_HTTP_PORT);
+ String domainName = sensors().get(HOSTNAME);
+ return scheme + "://" + domainName + ":" + port;
+ }
+}