Persisting management plane ID test coverage

Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/178588f3
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/178588f3
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/178588f3

Branch: refs/heads/master
Commit: 178588f387c4c3528299e7484221ffc2bb1d995e
Parents: e799ca9
Author: Svetoslav Neykov <svetoslav.ney...@cloudsoftcorp.com>
Authored: Mon Apr 3 10:32:54 2017 +0300
Committer: Svetoslav Neykov <svetoslav.ney...@cloudsoftcorp.com>
Committed: Tue Apr 11 17:58:59 2017 +0300

----------------------------------------------------------------------
 .../mgmt/ha/HighAvailabilityManagerImpl.java    |   6 +-
 .../rebind/PeriodicDeltaChangeListener.java     |   5 +
 .../core/mgmt/rebind/ManagementPlaneIdTest.java | 248 +++++++++++++++++++
 .../core/mgmt/rebind/RebindTestUtils.java       |  41 ++-
 .../transformer/CompoundTransformerTest.java    |   1 +
 5 files changed, 290 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/178588f3/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/HighAvailabilityManagerImpl.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/HighAvailabilityManagerImpl.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/HighAvailabilityManagerImpl.java
index f968c96..db65c9b 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/HighAvailabilityManagerImpl.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/HighAvailabilityManagerImpl.java
@@ -284,7 +284,7 @@ public class HighAvailabilityManagerImpl implements 
HighAvailabilityManager {
         boolean weAreRecognisedAsMaster = existingMaster!=null && 
ownNodeId.equals(existingMaster.getNodeId());
         boolean weAreMasterLocally = 
getInternalNodeState()==ManagementNodeState.MASTER;
         
-        updatePlaneId(planeRec);
+        updateLocalPlaneId(planeRec);
         
         // catch error in some tests where mgmt context has a different HA 
manager
         if (managementContext.getHighAvailabilityManager()!=this)
@@ -460,7 +460,7 @@ public class HighAvailabilityManagerImpl implements 
HighAvailabilityManager {
             registerPollTask();
     }
 
-    protected void updatePlaneId(ManagementPlaneSyncRecord existingMaster) {
+    protected void updateLocalPlaneId(ManagementPlaneSyncRecord 
existingMaster) {
         if (existingMaster.getPlaneId() != null) {
             
((LocalManagementContext)managementContext).setManagementPlaneId(existingMaster.getPlaneId());
         }
@@ -722,7 +722,7 @@ public class HighAvailabilityManagerImpl implements 
HighAvailabilityManager {
             return;
         }
         
-        updatePlaneId(memento);
+        updateLocalPlaneId(memento);
         
         String currMasterNodeId = memento.getMasterNodeId();
         ManagementNodeSyncRecord currMasterNodeRecord = 
memento.getManagementNodes().get(currMasterNodeId);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/178588f3/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/PeriodicDeltaChangeListener.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/PeriodicDeltaChangeListener.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/PeriodicDeltaChangeListener.java
index 68e47f7..e5d6a40 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/PeriodicDeltaChangeListener.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/PeriodicDeltaChangeListener.java
@@ -401,6 +401,11 @@ public class PeriodicDeltaChangeListener implements 
ChangeListener {
             if (!alreadyHasMutex) persistingMutex.acquire();
             if (!isActive() && state != ListenerState.STOPPING) return;
             
+            // Writes to the datastore are lossy. We'll just log failures and 
move on.
+            // (Most) entities will get updated multiple times in their 
lifecycle
+            // so not a huge deal. planeId does not get updated so if the first
+            // write fails it's not available to the HA cluster at all. That's 
why it
+            // gets periodically written to the datastore. 
             updatePlaneIdIfTimedOut();
 
             // Atomically switch the delta, so subsequent modifications will 
be done in the

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/178588f3/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/ManagementPlaneIdTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/ManagementPlaneIdTest.java
 
b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/ManagementPlaneIdTest.java
new file mode 100644
index 0000000..56ff7c0
--- /dev/null
+++ 
b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/ManagementPlaneIdTest.java
@@ -0,0 +1,248 @@
+/*
+ * 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.core.mgmt.rebind;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotEquals;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityMode;
+import org.apache.brooklyn.api.mgmt.ha.ManagementNodeState;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import 
org.apache.brooklyn.core.mgmt.persist.BrooklynMementoPersisterToObjectStore;
+import org.apache.brooklyn.core.mgmt.persist.FileBasedObjectStore;
+import org.apache.brooklyn.core.mgmt.persist.PersistMode;
+import org.apache.brooklyn.core.server.BrooklynServerConfig;
+import org.apache.brooklyn.core.server.BrooklynServerPaths;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.text.Strings;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class ManagementPlaneIdTest {
+    private File mementoDir;
+
+    protected ClassLoader classLoader = getClass().getClassLoader();
+
+    private Collection<ManagementContext> managementContextForTermination;
+
+    @BeforeMethod
+    public void setUp() {
+        mementoDir = Os.newTempDir(getClass());
+        managementContextForTermination = new ArrayList<>();
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (managementContextForTermination != null) {
+            for (ManagementContext mgmt : managementContextForTermination) {
+                Entities.destroyAll(mgmt);
+            }
+        }
+        if (mementoDir != null) 
FileBasedObjectStore.deleteCompletely(mementoDir);
+    }
+
+    @Test
+    public void testUninitializedThrows() {
+        ManagementContext mgmt = new 
LocalManagementContext(BrooklynProperties.Factory.newEmpty());
+        try {
+            mgmt.getManagementPlaneId();
+            Asserts.shouldHaveFailedPreviously("managementPlaneId not 
initialized");
+        } catch (NullPointerException e) {
+            Asserts.expectedFailureContains(e, "not initialized");
+        }
+    }
+    
+    @Test
+    public void testPlaneIdPersists() throws Exception {
+        final ManagementContext mgmt = 
createManagementContext(PersistMode.AUTO, HighAvailabilityMode.DISABLED);
+        checkPlaneIdPersisted(mgmt);
+    }
+
+    @Test
+    public void testPlaneIdRolledBack() throws Exception {
+        final LocalManagementContext mgmt = 
createManagementContext(PersistMode.AUTO, HighAvailabilityMode.AUTO);
+
+        checkPlaneIdPersisted(mgmt);
+        final String oldPlaneId = mgmt.getOptionalManagementPlaneId().get();
+        mgmt.setManagementPlaneId(Strings.makeRandomId(8));
+        assertNotEquals(oldPlaneId, mgmt.getOptionalManagementPlaneId().get());
+        Asserts.succeedsEventually(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(oldPlaneId, 
mgmt.getOptionalManagementPlaneId().get());
+            }
+        });
+    }
+
+    @Test
+    public void testColdRebindInitialisesPlaneId() throws Exception {
+        final LocalManagementContext origMgmt = 
createManagementContext(PersistMode.AUTO, HighAvailabilityMode.DISABLED);
+        checkPlaneIdPersisted(origMgmt);
+        Entities.destroyAll(origMgmt);
+
+        LocalManagementContext rebindMgmt = 
createManagementContext(PersistMode.AUTO, HighAvailabilityMode.DISABLED);
+
+        assertEquals(origMgmt.getManagementPlaneId(), 
rebindMgmt.getManagementPlaneId());
+    }
+
+
+    @DataProvider
+    public Object[][] haSlaveModes() {
+        return new Object[][] {
+            {HighAvailabilityMode.AUTO},
+            {HighAvailabilityMode.STANDBY},
+            {HighAvailabilityMode.HOT_STANDBY},
+            {HighAvailabilityMode.HOT_BACKUP},
+        };
+    }
+
+    @Test(dataProvider="haSlaveModes")
+    public void testHaRebindInitialisesPlaneId(HighAvailabilityMode slaveMode) 
throws Exception {
+        final LocalManagementContext origMgmt = 
createManagementContext(PersistMode.AUTO, HighAvailabilityMode.AUTO);
+        final LocalManagementContext rebindMgmt = 
createManagementContext(PersistMode.AUTO, slaveMode);
+
+        Asserts.succeedsEventually(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(origMgmt.getManagementPlaneId(), 
rebindMgmt.getManagementPlaneId());
+            }
+        });
+    }
+
+    @Test
+    public void testHaFailoverKeepsPlaneId() throws Exception {
+        final LocalManagementContext origMgmt = 
createManagementContext(PersistMode.AUTO, HighAvailabilityMode.MASTER);
+        final LocalManagementContext rebindMgmt = 
createManagementContext(PersistMode.AUTO, HighAvailabilityMode.STANDBY);
+
+        Asserts.succeedsEventually(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(origMgmt.getManagementPlaneId(), 
rebindMgmt.getManagementPlaneId());
+            }
+        });
+
+        Entities.destroyAll(origMgmt);
+
+        Asserts.succeedsEventually(new Runnable() {
+            @Override
+            public void run() {
+                
assertEquals(rebindMgmt.getHighAvailabilityManager().getNodeState(), 
ManagementNodeState.MASTER);
+                if (rebindMgmt.getRebindManager().isAwaitingInitialRebind()) {
+                    throw new IllegalStateException("still rebinding");
+                }
+            }
+        });
+
+        assertEquals(origMgmt.getManagementPlaneId(), 
rebindMgmt.getManagementPlaneId());
+    }
+    
+    @Test
+    public void testPlaneIdBackedUp() throws Exception {
+        final LocalManagementContext origMgmt = 
createManagementContext(PersistMode.AUTO, HighAvailabilityMode.AUTO);
+        checkPlaneIdPersisted(origMgmt);
+        Entities.destroyAll(origMgmt);
+
+        LocalManagementContext rebindMgmt = 
createManagementContextWithBackups(PersistMode.AUTO, HighAvailabilityMode.AUTO);
+
+        assertEquals(origMgmt.getManagementPlaneId(), 
rebindMgmt.getManagementPlaneId());
+
+        String backupContainer = 
BrooklynServerPaths.newBackupPersistencePathResolver(rebindMgmt).resolve();
+        
+        File[] promotionFolders = new File(backupContainer).listFiles(new 
FilenameFilter() {
+            @Override
+            public boolean accept(File dir, String name) {
+                return name.contains("promotion");
+            }
+        });
+        
+        assertEquals(promotionFolders.length, 1);
+        
+        File planeIdFile = new File(promotionFolders[0], 
BrooklynMementoPersisterToObjectStore.PLANE_ID_FILE_NAME);
+        String planeId = readFile(planeIdFile);
+        assertEquals(origMgmt.getManagementPlaneId(), planeId);
+    }
+    
+    @Test
+    public void testFullPersist() throws Exception {
+        final LocalManagementContext origMgmt = 
createManagementContext(PersistMode.DISABLED, HighAvailabilityMode.DISABLED);
+        origMgmt.getRebindManager().getPersister().enableWriteAccess();
+        origMgmt.getRebindManager().forcePersistNow(true, null);
+        checkPlaneIdPersisted(origMgmt);
+    }
+    
+    protected LocalManagementContext createManagementContext(PersistMode 
persistMode, HighAvailabilityMode haMode) {
+        return createManagementContext(persistMode, haMode, false);
+    }
+    
+    protected LocalManagementContext 
createManagementContextWithBackups(PersistMode persistMode, 
HighAvailabilityMode haMode) {
+        return createManagementContext(persistMode, haMode, true);
+    }
+    
+    protected LocalManagementContext createManagementContext(PersistMode 
persistMode, HighAvailabilityMode haMode, boolean backedUp) {
+        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+        props.put(BrooklynServerConfig.PERSISTENCE_BACKUPS_DIR, mementoDir);
+        LocalManagementContext mgmt = 
RebindTestUtils.managementContextBuilder(mementoDir, classLoader)
+                .persistPeriodMillis(1)
+                .persistMode(persistMode)
+                .haMode(haMode)
+                .enablePersistenceBackups(backedUp)
+                .emptyCatalog(true)
+                .properties(props)
+                .enableOsgi(false)
+                .buildStarted();
+        markForTermination(mgmt);
+        return mgmt;
+    }
+
+    private void markForTermination(ManagementContext mgmt) {
+        managementContextForTermination.add(mgmt);
+    }
+
+    protected static String readFile(File planeIdFile) throws IOException {
+        return new String(Files.readAllBytes(planeIdFile.toPath()), 
StandardCharsets.UTF_8);
+    }
+
+    protected void checkPlaneIdPersisted(final ManagementContext mgmt) {
+        final File planeIdFile = new File(mementoDir, 
BrooklynMementoPersisterToObjectStore.PLANE_ID_FILE_NAME);
+        Asserts.succeedsEventually(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                String planeId = readFile(planeIdFile);
+                assertEquals(mgmt.getManagementPlaneId(), planeId);
+                return null;
+            }
+        });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/178588f3/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestUtils.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestUtils.java 
b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestUtils.java
index 3f8e330..29ea68b 100644
--- 
a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestUtils.java
+++ 
b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestUtils.java
@@ -55,7 +55,6 @@ import 
org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.util.io.FileUtil;
 import org.apache.brooklyn.util.javalang.Serializers;
 import org.apache.brooklyn.util.javalang.Serializers.ObjectReplacer;
-import org.apache.brooklyn.util.text.Identifiers;
 import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -151,7 +150,8 @@ public class RebindTestUtils {
         BrooklynProperties properties;
         PersistenceObjectStore objectStore;
         Duration persistPeriod = Duration.millis(100);
-        HighAvailabilityMode haMode;
+        PersistMode persistMode = PersistMode.AUTO;
+        HighAvailabilityMode haMode = HighAvailabilityMode.DISABLED;
         boolean forLive;
         boolean enableOsgi = false;
         boolean emptyCatalog;
@@ -207,11 +207,25 @@ public class RebindTestUtils {
             return this;
         }
 
+        public ManagementContextBuilder persistMode(PersistMode val) {
+            checkNotNull(val, "persistMode");
+            this.persistMode = val;
+            if (persistMode == PersistMode.DISABLED) {
+                haMode(HighAvailabilityMode.DISABLED);
+            }
+            return this;
+        }
+
         public ManagementContextBuilder haMode(HighAvailabilityMode val) {
+            checkNotNull(val, "haMode");
             this.haMode = val;
             return this;
         }
 
+        /**
+         * What you could actually want is {@link #buildStarted()} with 
builder properties set to
+         * {@code 
.persistMode(PersistMode.DISABLED).haMode(HighAvailabilityMode.DISABLED)}
+         */
         public LocalManagementContext buildUnstarted() {
             LocalManagementContext unstarted;
             BrooklynProperties properties = this.properties != null
@@ -227,20 +241,22 @@ public class RebindTestUtils {
             }
             if (forLive) {
                 unstarted = new LocalManagementContext(properties);
-                unstarted.generateManagementPlaneId();
             } else {
-                unstarted = 
LocalManagementContextForTests.builder(true).useProperties(properties).disableOsgi(!enableOsgi).build();
+                unstarted = LocalManagementContextForTests.builder(true)
+                        .useProperties(properties)
+                        .disableOsgi(!enableOsgi)
+                        .disablePersistenceBackups(!enablePersistenceBackups)
+                        .build();
             }
             
             objectStore.injectManagementContext(unstarted);
-            objectStore.prepareForSharedUse(PersistMode.AUTO, (haMode == null 
? HighAvailabilityMode.DISABLED : haMode));
+            objectStore.prepareForSharedUse(PersistMode.AUTO, haMode);
             BrooklynMementoPersisterToObjectStore newPersister = new 
BrooklynMementoPersisterToObjectStore(
                     objectStore, 
                     unstarted.getBrooklynProperties(), 
                     classLoader);
             ((RebindManagerImpl) 
unstarted.getRebindManager()).setPeriodicPersistPeriod(persistPeriod);
             unstarted.getRebindManager().setPersister(newPersister, 
PersistenceExceptionHandlerImpl.builder().build());
-            unstarted.getHighAvailabilityManager().disabled();
             // set the HA persister, in case any children want to use HA
             unstarted.getHighAvailabilityManager().setPersister(new 
ManagementPlaneSyncRecordPersisterToObjectStore(unstarted, objectStore, 
classLoader));
             return unstarted;
@@ -248,8 +264,17 @@ public class RebindTestUtils {
 
         public LocalManagementContext buildStarted() {
             LocalManagementContext unstarted = buildUnstarted();
-            unstarted.getHighAvailabilityManager().disabled();
-            unstarted.getRebindManager().startPersistence();
+            // Follows BasicLauncher logic for initialising persistence.
+            // TODO It should really be encapsulated in a common entry point
+            if (persistMode == PersistMode.DISABLED) {
+                unstarted.generateManagementPlaneId();
+            } else if (haMode == HighAvailabilityMode.DISABLED) {
+                unstarted.getRebindManager().rebind(classLoader, null, 
ManagementNodeState.MASTER);
+                unstarted.getRebindManager().startPersistence();
+                unstarted.getHighAvailabilityManager().disabled();
+            } else {
+                unstarted.getHighAvailabilityManager().start(haMode);
+            }
             return unstarted;
         }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/178588f3/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/transformer/CompoundTransformerTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/transformer/CompoundTransformerTest.java
 
b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/transformer/CompoundTransformerTest.java
index 7187826..b742444 100644
--- 
a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/transformer/CompoundTransformerTest.java
+++ 
b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/transformer/CompoundTransformerTest.java
@@ -169,6 +169,7 @@ public class CompoundTransformerTest extends 
RebindTestFixtureWithApp {
 
         // Assert has expected config/fields
         assertEquals(newApp.getId(), origApp.getId());
+        assertEquals(origManagementContext.getManagementPlaneId(), 
newManagementContext.getManagementPlaneId());
     }
     
     @Test

Reply via email to