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 <alex.henev...@cloudsoftcorp.com>
Authored: Mon Jun 9 03:05:51 2014 +0100
Committer: Alex Heneveld <alex.henev...@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();
++    }
 +}

Reply via email to