changeset 1807:307c1670bb04

Report error if thermostat setup is run whilst storage is already running Reviewed-by: jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-September/016251.html PR2637
author Anirudhan Mukundan <amukunda@redhat.com>
date Tue, 22 Sep 2015 12:36:10 -0400
parents 5ac98bfdbd1e
children e4292781c523
files setup/command/pom.xml setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/SetupWindow.java setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/MongodbUserSetup.java setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/StampFiles.java setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/ThermostatSetup.java setup/command/src/main/java/com/redhat/thermostat/setup/command/locale/LocaleResources.java setup/command/src/main/resources/com/redhat/thermostat/setup/locale/strings.properties setup/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/MongodbUserSetupTest.java setup/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/ThermostatSetupTest.java setup/distribution/thermostat-plugin.xml
diffstat 10 files changed, 164 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/setup/command/pom.xml	Mon Sep 21 19:01:50 2015 +0200
+++ b/setup/command/pom.xml	Tue Sep 22 12:36:10 2015 -0400
@@ -117,6 +117,11 @@
       <artifactId>thermostat-launcher</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-osgi-process-handler</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 </project>
 
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/SetupWindow.java	Mon Sep 21 19:01:50 2015 +0200
+++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/SetupWindow.java	Tue Sep 22 12:36:10 2015 -0400
@@ -181,7 +181,8 @@
         doSynchronouslyOnEdt(new Runnable() {
             @Override
             public void run() {
-                ErrorDialog.createDialog(frame, e).setVisible(true);
+                String reason = thermostatSetup.determineReasonFromException(e);
+                ErrorDialog.createDialog(frame, reason, e).setVisible(true);
             }
         });
     }
@@ -438,7 +439,7 @@
 
     private static class ErrorDialog extends JDialog {
 
-        static JDialog createDialog(JFrame parent, Throwable throwable) {
+        static JDialog createDialog(JFrame parent, String reason, Throwable throwable) {
             JOptionPane optionPane = new JOptionPane();
             optionPane.setMessageType(JOptionPane.ERROR_MESSAGE);
 
@@ -460,7 +461,7 @@
             messagePanel.add(infoButtonPanel);
 
             optionPane.setMessage(new Object[] {
-                    translator.localize(LocaleResources.SETUP_FAILED_DIALOG_MESSAGE, throwable.getLocalizedMessage()).getContents(),
+                    translator.localize(LocaleResources.SETUP_FAILED_DIALOG_MESSAGE, reason).getContents(),
                     messagePanel,
             });
 
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/MongodbUserSetup.java	Mon Sep 21 19:01:50 2015 +0200
+++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/MongodbUserSetup.java	Tue Sep 22 12:36:10 2015 -0400
@@ -36,6 +36,7 @@
 
 package com.redhat.thermostat.setup.command.internal.model;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -44,6 +45,8 @@
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.lang.ProcessBuilder.Redirect;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
@@ -62,12 +65,14 @@
 import com.redhat.thermostat.common.utils.StreamUtils;
 import com.redhat.thermostat.launcher.Launcher;
 import com.redhat.thermostat.shared.config.CommonPaths;
+import com.redhat.thermostat.service.process.UnixProcessUtilities;
 
 class MongodbUserSetup implements UserSetup {
 
     static final String[] STORAGE_START_ARGS = {"storage", "--start", "--permitLocalhostException"};
     static final String[] STORAGE_STOP_ARGS = {"storage", "--stop"};
     private static final String WEB_AUTH_FILE = "web.auth";
+    private static final String MONGO_PROCESS = "mongod";
     private static final Logger logger = LoggingUtils.getLogger(MongodbUserSetup.class);
     private final UserCredsValidator validator;
     private final Launcher launcher;
@@ -81,6 +86,7 @@
     private String username;
     private char[] password;
     private String userComment;
+    private Integer pid;
     
     MongodbUserSetup(UserCredsValidator validator, Launcher launcher, CredentialFinder finder, CredentialsFileCreator fileCreator, CommonPaths paths, StampFiles stampFiles, StructureInformation structureInfo, AuthFileWriter authWriter, KeyringWriter keyringWriter) {
         this.validator = validator;
@@ -113,9 +119,12 @@
     }
     
     private void addMongodbUser() throws MongodbUserSetupException {
+        boolean thermostatUnlocked = false;
         boolean storageStarted = false;
         try {
-            unlockThermostat();
+            thermostatUnlocked = unlockThermostat();
+            
+            checkStorageNotRunning();
 
             storageStarted = startStorage();
 
@@ -152,12 +161,54 @@
             throw new MongodbUserSetupException("Error creating Mongodb user", e);
         } catch (MongodbUserSetupException e) {
             // Stop storage (if need be), remove temp stamp files and rethrow
-            cleanupAndReThrow(storageStarted, e);
+            cleanupAndReThrow(thermostatUnlocked, storageStarted, e);
         } finally {
             Arrays.fill(password, '\0'); // clear the password
         }
     }
+
+    private void checkStorageNotRunning() throws StorageAlreadyRunningException {
+        if (isStorageRunning()) {
+            throw new StorageAlreadyRunningException();
+        }
+    }
+
+    boolean isStorageRunning() {
+        File pidFile = paths.getUserStoragePidFile();
+        if (!checkPid(pidFile)) {
+            return false;
+        }
+        String processName = UnixProcessUtilities.getInstance().getProcessName(pid);
+        // TODO: check if we want mongos or mongod from the configs
+        return (processName != null && processName.equalsIgnoreCase(MONGO_PROCESS));
+    }
+
+    // package private for testing
+    boolean checkPid(File pidFile) {
+        Charset charset = Charset.defaultCharset();
+        if (pidFile.exists()) {
+            try (BufferedReader reader = Files.newBufferedReader(pidFile.toPath(), charset)) {
+                pid = doGetPid(reader);
+            } catch (IOException | NumberFormatException e) {
+                pid = null;
+            }
+        } else {
+            pid = null;
+        }
+        return (pid != null);
+    }
     
+    // package private for testing
+    Integer doGetPid(BufferedReader reader) throws IOException {
+        String line = reader.readLine();
+        // readLine() returns null on EOF
+        if (line == null || line.isEmpty()) {
+            return null;
+        } else {
+            return Integer.parseInt(line);
+        }
+    }
+
     private void setupForDirectMongodbUrls() throws MongodbUserSetupException {
         try {
             authFileWriter.setCredentials(username, Arrays.copyOf(password, password.length));
@@ -170,21 +221,28 @@
         }
     }
 
-    private void cleanupAndReThrow(boolean storageStarted, MongodbUserSetupException e) throws MongodbUserSetupException {
+    private void cleanupAndReThrow(boolean thermostatUnlocked, boolean storageStarted, MongodbUserSetupException e) throws MongodbUserSetupException {
         if (storageStarted) {
             stopStorage();
         }
-        stampFiles.deleteSetupCompleteStamp();
-        stampFiles.deleteMongodbUserStamp();
+        if (thermostatUnlocked) {
+            stampFiles.deleteSetupCompleteStamp();
+            stampFiles.deleteMongodbUserStamp();
+        }
         throw e;
     }
 
     //package-private for testing
-    void unlockThermostat() throws IOException {
+    boolean unlockThermostat() throws IOException {
+        if (stampFiles.setupCompleteStampExists()) {
+            // Stamp file exists so this is a re-run of setup.
+            return false;
+        }
         Date date = new Date();
         String timestamp = ThermostatSetup.DATE_FORMAT.format(date);
         String setupTmpUnlockContent = "Temporarily unlocked thermostat via '" + ThermostatSetup.PROGRAM_NAME + "' on " + timestamp + "\n";
         stampFiles.createSetupCompleteStamp(setupTmpUnlockContent);
+        return true;
     }
     
     //package-private for testing
@@ -384,4 +442,10 @@
         }
     }
 
+    public static class StorageAlreadyRunningException extends MongodbUserSetupException {
+        public StorageAlreadyRunningException() {
+            super("");
+        }
+    }
+
 }
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/StampFiles.java	Mon Sep 21 19:01:50 2015 +0200
+++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/StampFiles.java	Tue Sep 22 12:36:10 2015 -0400
@@ -78,6 +78,10 @@
         return new File(paths.getUserPersistentDataDirectory(), "setup-complete.stamp");
     }
     
+    boolean setupCompleteStampExists() {
+        return getSetupCompleteStampFile().exists();
+    }
+    
     private void deleteFile(File file) {
         try {
             Files.delete(file.toPath());
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/ThermostatSetup.java	Mon Sep 21 19:01:50 2015 +0200
+++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/internal/model/ThermostatSetup.java	Tue Sep 22 12:36:10 2015 -0400
@@ -42,7 +42,9 @@
 
 import com.redhat.thermostat.common.config.ClientPreferences;
 import com.redhat.thermostat.launcher.Launcher;
+import com.redhat.thermostat.setup.command.locale.LocaleResources;
 import com.redhat.thermostat.shared.config.CommonPaths;
+import com.redhat.thermostat.shared.locale.Translate;
 import com.redhat.thermostat.utils.keyring.Keyring;
 
 public class ThermostatSetup implements PersistableSetup {
@@ -55,6 +57,7 @@
     private static final String THERMOSTAT_CLIENT_REC_ROLE_NAME = "thermostat-client";
     private static final String THERMOSTAT_GRANT_CMD_CHANNEL_ALL_REC_ROLE_NAME = "thermostat-cmdc";
     private static final String THERMOSTAT_ADMIN_READ_ALL_REC_ROLE_NAME = "thermostat-admin-read-all";
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
     private final ThermostatUserSetup userSetup;
     private final MongodbUserSetup mongodbUserSetup;
     private final StructureInformation structureInfo;
@@ -123,6 +126,14 @@
     public boolean isWebAppInstalled() {
         return structureInfo.isWebAppInstalled();
     }
+
+    public String determineReasonFromException(Throwable e) {
+        if (e.getCause() instanceof MongodbUserSetup.StorageAlreadyRunningException) {
+            return translator.localize(LocaleResources.STORAGE_RUNNING).getContents();
+        } else {
+            return e.getLocalizedMessage();
+        }
+    }
     
     public static ThermostatSetup create(Launcher launcher, CommonPaths paths, Keyring keyring) {
         CredentialFinder finder = new CredentialFinder(paths);
--- a/setup/command/src/main/java/com/redhat/thermostat/setup/command/locale/LocaleResources.java	Mon Sep 21 19:01:50 2015 +0200
+++ b/setup/command/src/main/java/com/redhat/thermostat/setup/command/locale/LocaleResources.java	Tue Sep 22 12:36:10 2015 -0400
@@ -64,6 +64,7 @@
     THERMOSTAT_BRIEF,
     THERMOSTAT_BLURB,
     STORAGE_FAILED,
+    STORAGE_RUNNING,
     SERVICE_UNAVAILABLE_MESSAGE,
     SETUP_FAILED,
     SETUP_INTERRUPTED,
--- a/setup/command/src/main/resources/com/redhat/thermostat/setup/locale/strings.properties	Mon Sep 21 19:01:50 2015 +0200
+++ b/setup/command/src/main/resources/com/redhat/thermostat/setup/locale/strings.properties	Tue Sep 22 12:36:10 2015 -0400
@@ -63,6 +63,8 @@
 
 STORAGE_FAILED=Thermostat storage failed
 
+STORAGE_RUNNING=Thermostat storage is already running. Please stop storage and then run setup again.
+
 AGENT_HELP_INFO=Use these credentials for "thermostat agent" (File agent.auth). This wizard will set this up appropriately for you.
 
 STORAGE_HELP_INFO=These credentials are used by the mongodb storage
--- a/setup/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/MongodbUserSetupTest.java	Mon Sep 21 19:01:50 2015 +0200
+++ b/setup/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/MongodbUserSetupTest.java	Tue Sep 22 12:36:10 2015 -0400
@@ -38,7 +38,9 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
@@ -60,6 +62,7 @@
 import java.nio.file.Paths;
 import java.util.Collection;
 import java.util.Properties;
+import java.io.BufferedReader;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -105,6 +108,13 @@
                 //we need to always return 0 for success in tests
                 return 0;
             }
+
+            @Override
+            boolean isStorageRunning() {
+                // Storage is required to not already be running in
+                // order for mongodb user to be added.
+                return false;
+            }
         };
     }
     
@@ -130,6 +140,7 @@
     
     @Test
     public void testUnlockThermostat() throws IOException {
+        when(stampFiles.setupCompleteStampExists()).thenReturn(false);
         mongoSetup.unlockThermostat();
         ArgumentCaptor<String> argCaptor = ArgumentCaptor.forClass(String.class);
         verify(stampFiles).createSetupCompleteStamp(argCaptor.capture());
@@ -234,6 +245,13 @@
                     //return non-zero val to test failure
                     return 1;
                 }
+
+                @Override
+                boolean isStorageRunning() {
+                    // Storage is required to not already be running in
+                    // order for mongodb user to be added.
+                    return false;
+                }
             };
     
             doAnswer(new Answer<Void>() {
@@ -347,6 +365,13 @@
                     //we need to always return 0 for success in tests
                     return 0;
                 }
+
+                @Override
+                boolean isStorageRunning() {
+                    // Storage is required to not already be running in
+                    // order for mongodb user to be added.
+                    return false;
+                }
             };
             String username = "foo-user";
             char[] password = new char[] { 't', 'e', 's', 't' };
@@ -447,6 +472,13 @@
                     //we need to always return 0 for success in tests
                     return 0;
                 }
+
+                @Override
+                boolean isStorageRunning() {
+                    // Storage is required to not already be running in
+                    // order for mongodb user to be added.
+                    return false;
+                }
             };
             String username = "foo-user";
             char[] password = new char[] { 't', 'e', 's', 't' };
@@ -479,6 +511,21 @@
             TestRootHelper.recursivelyRemoveTestRootDirectory(testRoot);
         }
     }
+
+    @Test
+    public void testCheckPidIfFileDoesNotExist() {
+        File pidFile = mock(File.class);
+        when(pidFile.exists()).thenReturn(false);
+        assertFalse(mongoSetup.checkPid(pidFile));
+    }
+    
+    @Test
+    public void testDoGetPidNull() throws IOException {
+        BufferedReader reader = mock(BufferedReader.class);
+        when(reader.readLine()).thenReturn(null);
+        Integer pid = mongoSetup.doGetPid(reader);
+        assertNull(pid);
+    }
     
     private CharArrayMatcher matchesPassword(char[] expected) {
         return new CharArrayMatcher(expected);
--- a/setup/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/ThermostatSetupTest.java	Mon Sep 21 19:01:50 2015 +0200
+++ b/setup/command/src/test/java/com/redhat/thermostat/setup/command/internal/model/ThermostatSetupTest.java	Tue Sep 22 12:36:10 2015 -0400
@@ -131,6 +131,25 @@
         verifyNoMoreInteractions(userSetup);
     }
     
+    @Test
+    public void testDetermineReasonFromExceptionStorageRunningException() {
+        ThermostatSetup setup = new ThermostatSetup(userSetup, mongoUserSetup, mock(StructureInformation.class), mock(AuthFileWriter.class), mock(KeyringWriter.class));
+        Throwable testException = mock(Throwable.class);
+        when(testException.getCause()).thenReturn(mock(MongodbUserSetup.StorageAlreadyRunningException.class));
+        String reason = setup.determineReasonFromException(testException);
+        assertTrue(reason.equals("Thermostat storage is already running. Please stop storage and then run setup again."));
+    }
+
+    @Test
+    public void testDetermineReasonFromExceptionGenericException() {
+        ThermostatSetup setup = new ThermostatSetup(userSetup, mongoUserSetup, mock(StructureInformation.class), mock(AuthFileWriter.class), mock(KeyringWriter.class));
+        Throwable testException = mock(Throwable.class);
+        when(testException.getCause()).thenReturn(mock(Exception.class));
+        when(testException.getLocalizedMessage()).thenReturn("test error message");
+        String reason = setup.determineReasonFromException(testException);
+        assertTrue(reason.equals("test error message"));
+    }
+    
     private static class RoleMatcher extends BaseMatcher<String[]> {
         
         final String[] expected;
--- a/setup/distribution/thermostat-plugin.xml	Mon Sep 21 19:01:50 2015 +0200
+++ b/setup/distribution/thermostat-plugin.xml	Tue Sep 22 12:36:10 2015 -0400
@@ -70,6 +70,7 @@
         <bundle><symbolic-name>com.redhat.thermostat.client.core</symbolic-name><version>${project.version}</version></bundle>
         <bundle><symbolic-name>com.redhat.thermostat.internal.utils.laf</symbolic-name><version>${project.version}</version></bundle>
         <bundle><symbolic-name>com.redhat.thermostat.plugin.validator</symbolic-name><version>${project.version}</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.process</symbolic-name><version>${project.version}</version></bundle>
       </bundles>
     </command>
   </commands>