changeset 1533:d5a7cb4fab04

Do not purge by default and warn user of potential clean-data impact This is short term solution to PR2039. The default agent configuration is changed so that data is *not* purged on exit, as this can be disruptive to other clients of the Storage node. Furthermore, a warning about this disruption is added to the clean-data command. reviewed-by: jerboaa review-thread: http://icedtea.classpath.org/pipermail/thermostat/2014-October/011337.html
author Jon VanAlten <jon.vanalten@redhat.com>
date Tue, 28 Oct 2014 11:39:23 -0600
parents 8dd9bcaee77e
children 50c227cb0b02
files client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/CleanDataCommand.java client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/CleanDataCommandTest.java distribution/config/agent.properties
diffstat 5 files changed, 95 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/CleanDataCommand.java	Tue Oct 28 17:00:23 2014 -0400
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/CleanDataCommand.java	Tue Oct 28 11:39:23 2014 -0600
@@ -36,6 +36,10 @@
 
 package com.redhat.thermostat.client.cli.internal;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.PrintStream;
 import java.util.List;
 
@@ -64,6 +68,18 @@
 
     @Override
     public void run(CommandContext ctx) throws CommandException {
+        InputStream input = ctx.getConsole().getInput();
+        PrintStream output = ctx.getConsole().getOutput();
+        try {
+            if (!userConfirmsCleanOperation(input, output)) {
+                indicateCleanCancelled(output);
+                return;
+            }
+        } catch (IOException ex) {
+            // We were not able to confirm operation with user.
+            indicateCleanCancelled(output);
+            return;
+        }
         ServiceReference storageServiceRef = bundleContext.getServiceReference(Storage.class);
         requireNonNull(storageServiceRef, translator.localize(LocaleResources.STORAGE_UNAVAILABLE));
         Storage storage = (Storage) bundleContext.getService(storageServiceRef);
@@ -72,7 +88,6 @@
             Arguments args = ctx.getArguments();
             List<String> agentIdList = args.getNonOptionArguments();
             removeLiveAgent = args.hasArgument(CleanOptions.ALIVE.option);
-            PrintStream output = ctx.getConsole().getOutput();
             
             if (args.hasArgument(CleanOptions.ALL.option)) {
                 removeDataForAllAgents(storage, output);
@@ -140,5 +155,26 @@
         }
     }
 
+    private boolean userConfirmsCleanOperation(InputStream input, PrintStream output) throws IOException {
+        output.println(translator.localize(LocaleResources.PURGE_EXPENSIVE_OPERATION_WARNING).getContents());
+        output.print(translator.localize(LocaleResources.PURGE_EXPENSIVE_OPERATION_PROMPT).getContents());
+        String userInput = new BufferedReader(new InputStreamReader(input)).readLine();
+        for (String affirmative : affirmativeResponses()) {
+            if (affirmative.equals(userInput.toLowerCase())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private String[] affirmativeResponses() {
+        String yesses = translator.localize(LocaleResources.AFFIRMATIVE_RESPONSES).getContents();
+        return yesses.split("|");
+    }
+
+    private void indicateCleanCancelled(PrintStream output) {
+        output.println(translator.localize(LocaleResources.PURGE_CANCELLED_MESSAGE).getContents());
+    }
+
 }
 
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java	Tue Oct 28 17:00:23 2014 -0400
+++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java	Tue Oct 28 11:39:23 2014 -0600
@@ -101,6 +101,10 @@
     AGENT_UNAVAILABLE,
     CANNOT_PURGE_AGENT_RUNNING,
     AGENT_NOT_FOUND,
+    PURGE_EXPENSIVE_OPERATION_WARNING,
+    PURGE_EXPENSIVE_OPERATION_PROMPT,
+    PURGE_CANCELLED_MESSAGE,
+    AFFIRMATIVE_RESPONSES,
     ;
 
     static final String RESOURCE_BUNDLE = "com.redhat.thermostat.client.cli.strings";
--- a/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties	Tue Oct 28 17:00:23 2014 -0400
+++ b/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties	Tue Oct 28 11:39:23 2014 -0600
@@ -60,3 +60,7 @@
 AGENT_UNAVAILABLE = Agent is unavailable
 CANNOT_PURGE_AGENT_RUNNING = Cannot purge data for agent {0}. This agent is currently running
 AGENT_NOT_FOUND = Agent with an id [{0}] was not found!
+PURGE_EXPENSIVE_OPERATION_WARNING = Cleaning a lot of data from Thermostat storage can cause latency for other agents or clients.
+PURGE_EXPENSIVE_OPERATION_PROMPT = Are you sure you want to continue (Y/y/N/n)?
+PURGE_CANCELLED_MESSAGE = Not cleaning Thermostat data at this time.
+AFFIRMATIVE_RESPONSES = y|yes
--- a/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/CleanDataCommandTest.java	Tue Oct 28 17:00:23 2014 -0400
+++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/CleanDataCommandTest.java	Tue Oct 28 11:39:23 2014 -0600
@@ -41,8 +41,11 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
 import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -68,6 +71,7 @@
     private CleanDataCommand cleanDataCommand;
     private CommandContext mockCommandContext;
     private Storage mockStorage;
+    private Console mockConsole;
     private PrintStream mockOutput;
     private AgentInfoDAO mockAgentInfoDAO;
     private Arguments mockArguments;
@@ -90,7 +94,7 @@
         when(mockCommandContext.getArguments()).thenReturn(mockArguments);
         when(mockArguments.getNonOptionArguments()).thenReturn(getValidAgentList());
         
-        Console mockConsole = mock(Console.class);
+        mockConsole = mock(Console.class);
         mockOutput = mock(PrintStream.class);
         when(mockCommandContext.getConsole()).thenReturn(mockConsole);
         when(mockConsole.getOutput()).thenReturn(mockOutput);
@@ -124,6 +128,7 @@
 
     @Test
     public void testOneValidArgument() throws CommandException {
+        setupUserConfirmation(new byte[]{ 'Y' });
         when(mockArguments.getNonOptionArguments()).thenReturn(getOneValidAgent());
         
         cleanDataCommand.run(mockCommandContext);
@@ -131,12 +136,15 @@
         verify(mockStorage, times(1)).purge(isA(String.class));
         verify(mockStorage).purge("agentId1");
         
-        verify(mockOutput, times(1)).println(isA(String.class));
+        verify(mockOutput, times(2)).println(isA(String.class));
+        verify(mockOutput, times(1)).print(isA(String.class));
         verify(mockOutput).println("Purging data for agent: agentId1");
     }
 
     @Test
     public void testMultipleValidArguments() throws CommandException {
+        setupUserConfirmation(new byte[]{ 'Y' });
+
         cleanDataCommand.run(mockCommandContext);
         
         verify(mockStorage, times(3)).purge(isA(String.class));
@@ -144,7 +152,10 @@
         verify(mockStorage).purge("agentId2");
         verify(mockStorage).purge("agentId3");
         
-        verify(mockOutput, times(3)).println(isA(String.class));
+        verify(mockOutput, times(4)).println(isA(String.class));
+        verify(mockOutput, times(1)).print(isA(String.class));
+        verify(mockOutput).println("Cleaning a lot of data from Thermostat storage can cause latency for other agents or clients.");
+        verify(mockOutput).print("Are you sure you want to continue (Y/y/N/n)?");
         verify(mockOutput).println("Purging data for agent: agentId1");
         verify(mockOutput).println("Purging data for agent: agentId2");
         verify(mockOutput).println("Purging data for agent: agentId3");
@@ -152,19 +163,24 @@
 
     @Test
     public void testUnauthorizedLiveAgents() throws CommandException {
+        setupUserConfirmation(new byte[]{ 'Y' });
         when(mockArguments.getNonOptionArguments()).thenReturn(getAliveAgents());
         
         cleanDataCommand.run(mockCommandContext);
         
         verify(mockStorage, never()).purge(isA(String.class));
         
-        verify(mockOutput, times(2)).println(isA(String.class));
+        verify(mockOutput, times(3)).println(isA(String.class));
+        verify(mockOutput, times(1)).print(isA(String.class));
+        verify(mockOutput).println("Cleaning a lot of data from Thermostat storage can cause latency for other agents or clients.");
+        verify(mockOutput).print("Are you sure you want to continue (Y/y/N/n)?");
         verify(mockOutput).println("Cannot purge data for agent agentId4. This agent is currently running");
         verify(mockOutput).println("Cannot purge data for agent agentId5. This agent is currently running");
     }
 
     @Test
     public void testRemoveSpecificLiveAgents() throws CommandException {
+        setupUserConfirmation(new byte[]{ 'Y' });
         when(mockArguments.hasArgument("alive")).thenReturn(true);
         when(mockArguments.getNonOptionArguments()).thenReturn(getAliveAgents());
         
@@ -174,19 +190,26 @@
         verify(mockStorage).purge("agentId4");
         verify(mockStorage).purge("agentId5");
         
-        verify(mockOutput, times(2)).println(isA(String.class));
+        verify(mockOutput, times(3)).println(isA(String.class));
+        verify(mockOutput, times(1)).print(isA(String.class));
+        verify(mockOutput).println("Cleaning a lot of data from Thermostat storage can cause latency for other agents or clients.");
+        verify(mockOutput).print("Are you sure you want to continue (Y/y/N/n)?");
         verify(mockOutput).println("Purging data for agent: agentId4");
         verify(mockOutput).println("Purging data for agent: agentId5");
     }
 
     @Test
     public void testInvalidArguments() throws CommandException {
+        setupUserConfirmation(new byte[]{ 'Y' });
         when(mockArguments.getNonOptionArguments()).thenReturn(getInvalidAgentList());
         
         cleanDataCommand.run(mockCommandContext);
         
         verify(mockStorage, never()).purge(isA(String.class));
-        verify(mockOutput, times(3)).println(isA(String.class));
+        verify(mockOutput, times(4)).println(isA(String.class));
+        verify(mockOutput, times(1)).print(isA(String.class));
+        verify(mockOutput).println("Cleaning a lot of data from Thermostat storage can cause latency for other agents or clients.");
+        verify(mockOutput).print("Are you sure you want to continue (Y/y/N/n)?");
         verify(mockOutput).println("Agent with an id [invalidAgent1] was not found!");
         verify(mockOutput).println("Agent with an id [invalidAgent2] was not found!");
         verify(mockOutput).println("Agent with an id [invalidAgent3] was not found!");
@@ -194,6 +217,7 @@
 
     @Test
     public void testRemoveAllDeadAgents() throws CommandException {
+        setupUserConfirmation(new byte[]{ 'Y' });
         when(mockArguments.hasArgument("all")).thenReturn(true);
         when(mockAgentInfoDAO.getAllAgentInformation()).thenReturn(allAgentInfoList);
         
@@ -207,6 +231,7 @@
 
     @Test
     public void testRemoveAllLiveAgents() throws CommandException {
+        setupUserConfirmation(new byte[]{ 'Y' });
         when(mockArguments.hasArgument("all")).thenReturn(true);
         when(mockArguments.hasArgument("alive")).thenReturn(true);
         when(mockAgentInfoDAO.getAllAgentInformation()).thenReturn(allAgentInfoList);
@@ -221,6 +246,20 @@
         verify(mockStorage).purge("agentId5");
     }
 
+    @Test
+    public void testUserRefusesClean() throws CommandException {
+        setupUserConfirmation(new byte[]{ 'N' });
+
+        cleanDataCommand.run(mockCommandContext);
+
+        verifyZeroInteractions(mockStorage);
+        verify(mockOutput, times(2)).println(isA(String.class));
+        verify(mockOutput, times(1)).print(isA(String.class));
+        verify(mockOutput).println("Cleaning a lot of data from Thermostat storage can cause latency for other agents or clients.");
+        verify(mockOutput).print("Are you sure you want to continue (Y/y/N/n)?");
+        verify(mockOutput).println("Not cleaning Thermostat data at this time.");
+    }
+
     private List<String> getValidAgentList() {
         List<String> agentIdList = new ArrayList<String>();
         agentIdList.add("agentId1");
@@ -250,5 +289,9 @@
         return agentIdList;
     }
 
+    private void setupUserConfirmation(byte[] response) {
+        InputStream input = new ByteArrayInputStream(response);
+        when(mockConsole.getInput()).thenReturn(input);
+    }
 }
 
--- a/distribution/config/agent.properties	Tue Oct 28 17:00:23 2014 -0400
+++ b/distribution/config/agent.properties	Tue Oct 28 11:39:23 2014 -0600
@@ -1,6 +1,6 @@
 # Indicates if this agent will save its data to the database on exit
 # or rather will purge the db
-SAVE_ON_EXIT=false
+SAVE_ON_EXIT=true
 
 # A netty-based side channel for accepting configuration/tuning
 # requests from the client will listen for connections on the address