You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ri...@apache.org on 2014/06/10 02:15:30 UTC
[09/10] git commit: Merge branch 'master' into
object-store-persistence
Merge branch 'master' into object-store-persistence
Conflicts:
core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerTestFixture.java
-- new test added to master, integrated with method rename here and tested with refactoring
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/e6e59f7e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/e6e59f7e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/e6e59f7e
Branch: refs/heads/master
Commit: e6e59f7e911ef6f65062677b48e9a259f1deefb3
Parents: de3fb4b ab3e297
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Jun 9 03:05:51 2014 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Jun 9 03:34:25 2014 +0100
----------------------------------------------------------------------
.../catalog/internal/BasicBrooklynCatalog.java | 22 +-
.../catalog/internal/CatalogClasspathDo.java | 2 +-
.../brooklyn/catalog/internal/CatalogDo.java | 18 ++
.../brooklyn/config/render/RendererHints.java | 1 -
.../brooklyn/entity/basic/AbstractEntity.java | 5 +
.../brooklyn/entity/basic/EntityInternal.java | 6 +
.../brooklyn/entity/group/DynamicCluster.java | 2 +-
.../entity/group/DynamicClusterImpl.java | 46 +++-
.../brooklyn/entity/group/QuarantineGroup.java | 14 +
.../entity/group/QuarantineGroupImpl.java | 70 +++++
.../dto/BasicManagementNodeSyncRecord.java | 2 +-
.../location/access/BrooklynAccessUtils.java | 4 +-
.../location/access/PortForwardManager.java | 266 +++++++------------
.../access/PortForwardManagerAuthority.java | 233 ++++++++++++++++
.../access/PortForwardManagerClient.java | 139 ++++++++++
.../brooklyn/location/access/PortMapping.java | 16 ++
.../ha/HighAvailabilityManagerImpl.java | 32 ++-
.../util/xstream/StringKeyMapConverter.java | 6 +-
.../entity/group/DynamicClusterTest.java | 13 +
.../entity/group/QuarantineGroupTest.java | 74 ++++++
.../entity/rebind/RebindCatalogEntityTest.java | 6 +
.../access/PortForwardManagerRebindTest.java | 95 +++++++
.../ha/HighAvailabilityManagerTestFixture.java | 26 +-
.../util/xstream/StringKeyMapConverterTest.java | 7 +
docs/start/walkthrough/index.md | 4 +-
docs/use/guide/quickstart/brooklyn.properties | 34 +++
...ailabilityManagerJcloudsObjectStoreTest.java | 7 +-
.../java/JavaSoftwareProcessSshDriver.java | 3 +-
.../java/brooklyn/entity/java/JmxSupport.java | 7 +-
.../entity/proxy/nginx/NginxSshDriver.java | 3 +
.../ControlledDynamicWebAppClusterImpl.java | 75 ++++--
.../entity/webapp/tomcat/Tomcat7SshDriver.java | 12 +-
.../java/brooklyn/util/text/StringEscapes.java | 4 -
.../brooklyn/util/text/StringEscapesTest.java | 7 +-
34 files changed, 1030 insertions(+), 231 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e6e59f7e/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
----------------------------------------------------------------------
diff --cc core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
index 5762268,891cb1a..ee6427d
--- a/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
+++ b/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
@@@ -491,6 -485,24 +497,24 @@@ public class HighAvailabilityManagerImp
* specific timing scenarios.
*/
protected long currentTimeMillis() {
- return ticker.read();
+ return tickerUtc.read();
}
+
+ /**
+ * Infers the health of a node - if it last reported itself as healthy (standby or master), but we haven't heard
+ * from it in a long time then report that node as failed; otherwise report its health as-is.
+ */
+ private class DetectNodesGoneAwol implements Function<ManagementNodeSyncRecord, ManagementNodeSyncRecord> {
+ @Nullable
+ @Override
+ public ManagementNodeSyncRecord apply(@Nullable ManagementNodeSyncRecord input) {
+ if (input == null) return null;
+ if (!(input.getStatus() == ManagementNodeState.STANDBY || input.getStatus() == ManagementNodeState.MASTER)) return input;
+ if (isHeartbeatOk(input, currentTimeMillis())) return input;
+ return BasicManagementNodeSyncRecord.builder()
+ .from(input)
+ .status(ManagementNodeState.FAILED)
+ .build();
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e6e59f7e/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerTestFixture.java
----------------------------------------------------------------------
diff --cc core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerTestFixture.java
index 6b0450d,0000000..9f7601a
mode 100644,000000..100644
--- a/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerTestFixture.java
+++ b/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerTestFixture.java
@@@ -1,190 -1,0 +1,214 @@@
+package brooklyn.management.ha;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
++import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.BrooklynVersion;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
+import brooklyn.entity.rebind.persister.PersistMode;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore;
+import brooklyn.entity.rebind.plane.dto.BasicManagementNodeSyncRecord;
+import brooklyn.management.ha.HighAvailabilityManagerImpl.PromotionListener;
+import brooklyn.management.internal.ManagementContextInternal;
+import brooklyn.test.Asserts;
+import brooklyn.test.entity.LocalManagementContextForTests;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Ticker;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
+@Test
+public abstract class HighAvailabilityManagerTestFixture {
+
+ @SuppressWarnings("unused")
+ private static final Logger log = LoggerFactory.getLogger(HighAvailabilityManagerTestFixture.class);
+
+ private ManagementPlaneSyncRecordPersister persister;
+ private ManagementContextInternal managementContext;
+ private String ownNodeId;
+ private HighAvailabilityManagerImpl manager;
+ private Ticker ticker;
+ private AtomicLong currentTime; // used to set the ticker's return value
+ private RecordingPromotionListener promotionListener;
+ private ClassLoader classLoader = getClass().getClassLoader();
+ private PersistenceObjectStore objectStore;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ currentTime = new AtomicLong(System.currentTimeMillis());
+ ticker = new Ticker() {
+ // strictly not a ticker because returns millis UTC, but it works fine even so
+ @Override public long read() {
+ return currentTime.get();
+ }
+ };
+ promotionListener = new RecordingPromotionListener();
+ managementContext = newLocalManagementContext();
+ ownNodeId = managementContext.getManagementNodeId();
+ objectStore = newPersistenceObjectStore();
+ objectStore.prepareForUse(managementContext, PersistMode.CLEAN);
+ persister = new ManagementPlaneSyncRecordPersisterToObjectStore(managementContext, objectStore, classLoader);
+ BrooklynMementoPersisterToObjectStore persisterObj = new BrooklynMementoPersisterToObjectStore(objectStore, classLoader);
+ managementContext.getRebindManager().setPersister(persisterObj);
+ manager = new HighAvailabilityManagerImpl(managementContext)
+ .setPollPeriod(Duration.millis(10))
+ .setHeartbeatTimeout(Duration.THIRTY_SECONDS)
+ .setPromotionListener(promotionListener)
+ .setTicker(ticker)
+ .setPersister(persister);
+ }
+
+ protected ManagementContextInternal newLocalManagementContext() {
+ return new LocalManagementContextForTests();
+ }
+
+ protected abstract PersistenceObjectStore newPersistenceObjectStore();
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ if (manager != null) manager.stop();
+ if (managementContext != null) Entities.destroyAll(managementContext);
+ if (objectStore != null) objectStore.deleteCompletely();
+ }
+
+ // Can get a log.error about our management node's heartbeat being out of date. Caused by
+ // poller first writing a heartbeat record, and then the clock being incremented. But the
+ // next poll fixes it.
+ public void testPromotes() throws Exception {
+ persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
+ .node(newManagerMemento(ownNodeId, ManagementNodeState.STANDBY, tickerCurrentMillis()))
+ .node(newManagerMemento("node1", ManagementNodeState.MASTER, tickerCurrentMillis()))
+ .setMaster("node1")
+ .build());
+
+ manager.start(HighAvailabilityMode.AUTO);
+
+ // Simulate passage of time; ticker used by this HA-manager so it will "correctly" publish
+ // its own heartbeat with the new time; but node1's record is now out-of-date.
+ tickerAdvance(Duration.seconds(31));
+
+ // Expect to be notified of our promotion, as the only other node
+ promotionListener.assertCalledEventually();
+ }
+
+ @Test(groups="Integration") // because one second wait in succeedsContinually
+ public void testDoesNotPromoteIfMasterTimeoutNotExpired() throws Exception {
+ persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
+ .node(newManagerMemento(ownNodeId, ManagementNodeState.STANDBY, tickerCurrentMillis()))
+ .node(newManagerMemento("node1", ManagementNodeState.MASTER, tickerCurrentMillis()))
+ .setMaster("node1")
+ .build());
+
+ manager.start(HighAvailabilityMode.AUTO);
+
+ tickerAdvance(Duration.seconds(29));
+
+ // Expect not to be notified, as 29s < 30s timeout (it's a fake clock so won't hit 30, even waiting 1s below)
+ Asserts.succeedsContinually(new Runnable() {
+ @Override public void run() {
+ assertTrue(promotionListener.callTimestamps.isEmpty(), "calls="+promotionListener.callTimestamps);
+ }});
+ }
+
+ public void testGetManagementPlaneStatus() throws Exception {
+ // with the name zzzzz the mgr created here should never be promoted by the alphabetical strategy!
+
+ tickerAdvance(Duration.FIVE_SECONDS);
+ persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
+ .node(newManagerMemento("zzzzzzz_node1", ManagementNodeState.STANDBY, tickerCurrentMillis()))
+ .build());
+ long zzzTime = tickerCurrentMillis();
+ tickerAdvance(Duration.FIVE_SECONDS);
+
+ manager.start(HighAvailabilityMode.AUTO);
+ ManagementPlaneSyncRecord memento = manager.getManagementPlaneSyncState();
+
+ // Note can assert timestamp because not "real" time; it's using our own Ticker
+ assertEquals(memento.getMasterNodeId(), ownNodeId);
+ assertEquals(memento.getManagementNodes().keySet(), ImmutableSet.of(ownNodeId, "zzzzzzz_node1"));
+ assertEquals(memento.getManagementNodes().get(ownNodeId).getNodeId(), ownNodeId);
+ assertEquals(memento.getManagementNodes().get(ownNodeId).getStatus(), ManagementNodeState.MASTER);
+ assertEquals(memento.getManagementNodes().get(ownNodeId).getTimestampUtc(), tickerCurrentMillis());
+ assertEquals(memento.getManagementNodes().get("zzzzzzz_node1").getNodeId(), "zzzzzzz_node1");
+ assertEquals(memento.getManagementNodes().get("zzzzzzz_node1").getStatus(), ManagementNodeState.STANDBY);
+ assertEquals(memento.getManagementNodes().get("zzzzzzz_node1").getTimestampUtc(), zzzTime);
+ }
+
+ @Test(groups="Integration", invocationCount=50) //because we have had non-deterministic failures
+ public void testGetManagementPlaneStatusManyTimes() throws Exception {
+ testGetManagementPlaneStatus();
+ }
+
++ @Test
++ public void testGetManagementPlaneSyncStateInfersTimedOutNodeAsFailed() throws Exception {
++ persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
++ .node(newManagerMemento(ownNodeId, ManagementNodeState.STANDBY, tickerCurrentMillis()))
++ .node(newManagerMemento("node1", ManagementNodeState.MASTER, tickerCurrentMillis()))
++ .setMaster("node1")
++ .build());
++
++ manager.start(HighAvailabilityMode.AUTO);
++
++ ManagementPlaneSyncRecord state = manager.getManagementPlaneSyncState();
++ assertEquals(state.getManagementNodes().get("node1").getStatus(), ManagementNodeState.MASTER);
++ assertEquals(state.getManagementNodes().get(ownNodeId).getStatus(), ManagementNodeState.STANDBY);
++
++ // Simulate passage of time; ticker used by this HA-manager so it will "correctly" publish
++ // its own heartbeat with the new time; but node1's record is now out-of-date.
++ tickerAdvance(Duration.seconds(31));
++
++ ManagementPlaneSyncRecord state2 = manager.getManagementPlaneSyncState();
++ assertEquals(state2.getManagementNodes().get("node1").getStatus(), ManagementNodeState.FAILED);
++ assertNotEquals(state.getManagementNodes().get(ownNodeId).getStatus(), ManagementNodeState.FAILED);
++ }
++
+ private long tickerCurrentMillis() {
+ return ticker.read();
+ }
+
+ private long tickerAdvance(Duration duration) {
+ currentTime.addAndGet(duration.toMilliseconds());
+ return tickerCurrentMillis();
+ }
-
++
+ private ManagementNodeSyncRecord newManagerMemento(String nodeId, ManagementNodeState status, long timestamp) {
+ return BasicManagementNodeSyncRecord.builder().brooklynVersion(BrooklynVersion.get()).nodeId(nodeId).status(status).timestampUtc(timestamp).build();
+ }
+
+ public static class RecordingPromotionListener implements PromotionListener {
+ public final List<Long> callTimestamps = Lists.newCopyOnWriteArrayList();
+
+ @Override
+ public void promotingToMaster() {
+ callTimestamps.add(System.currentTimeMillis());
+ }
+
+ public void assertNotCalled() {
+ assertTrue(callTimestamps.isEmpty(), "calls="+callTimestamps);
+ }
+
+ public void assertCalled() {
+ assertFalse(callTimestamps.isEmpty(), "calls="+callTimestamps);
+ }
+
+ public void assertCalledEventually() {
+ Asserts.succeedsEventually(new Runnable() {
+ @Override public void run() {
+ assertCalled();
+ }});
+ }
+ };
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e6e59f7e/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/HighAvailabilityManagerJcloudsObjectStoreTest.java
----------------------------------------------------------------------
diff --cc locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/HighAvailabilityManagerJcloudsObjectStoreTest.java
index 17c5adc,0000000..b87b78d
mode 100644,000000..100644
--- a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/HighAvailabilityManagerJcloudsObjectStoreTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/HighAvailabilityManagerJcloudsObjectStoreTest.java
@@@ -1,51 -1,0 +1,56 @@@
+package brooklyn.entity.rebind.persister.jclouds;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore;
+import brooklyn.management.ha.HighAvailabilityManagerTestFixture;
+import brooklyn.management.internal.ManagementContextInternal;
+import brooklyn.test.entity.LocalManagementContextForTests;
+import brooklyn.util.text.Identifiers;
+
+@Test(groups="Integration")
+public class HighAvailabilityManagerJcloudsObjectStoreTest extends HighAvailabilityManagerTestFixture {
+
+ protected ManagementContextInternal newLocalManagementContext() {
+ return new LocalManagementContextForTests(BrooklynProperties.Factory.newDefault());
+ }
+
+ @Override @BeforeMethod
+ public void setUp() throws Exception { super.setUp(); }
+
+ protected PersistenceObjectStore newPersistenceObjectStore() {
+ return new JcloudsBlobStoreBasedObjectStore(
+ BlobStoreTest.PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC, BlobStoreTest.CONTAINER_PREFIX+"-"+Identifiers.makeRandomId(4));
+ }
+
+ @Test(groups="Integration", invocationCount=5) //run fewer times w softlayer...
+ public void testGetManagementPlaneStatusManyTimes() throws Exception {
+ testGetManagementPlaneStatus();
+ }
+
+ @Test(groups="Integration")
+ @Override
+ public void testDoesNotPromoteIfMasterTimeoutNotExpired() throws Exception {
+ super.testDoesNotPromoteIfMasterTimeoutNotExpired();
+ }
+
+ @Test(groups="Integration")
+ @Override
+ public void testGetManagementPlaneStatus() throws Exception {
+ super.testGetManagementPlaneStatus();
+ }
+
+ @Test(groups="Integration")
+ @Override
+ public void testPromotes() throws Exception {
+ super.testPromotes();
+ }
-
++
++ @Test(groups="Integration")
++ @Override
++ public void testGetManagementPlaneSyncStateInfersTimedOutNodeAsFailed() throws Exception {
++ super.testGetManagementPlaneSyncStateInfersTimedOutNodeAsFailed();
++ }
+}