Mercurial > hg > release > thermostat-1.4
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
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>