# HG changeset patch # User Elliott Baron # Date 1375311497 14400 # Node ID 1925258e63090faf5ebe7cce9d0b351b4113fa57 # Parent b525881b4f8afa88ef183ab3347827a5e5525004# Parent 56e89524791ceec7d35bd004f0bfd2e8055fe0d2 Merge diff -r b525881b4f8a -r 1925258e6309 client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/Activator.java --- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/Activator.java Wed Jul 31 15:53:15 2013 -0400 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/Activator.java Wed Jul 31 18:58:17 2013 -0400 @@ -56,6 +56,7 @@ reg.registerCommand("vm-stat", new VMStatCommand()); reg.registerCommand("disconnect", new DisconnectCommand()); reg.registerCommand("connect", new ConnectCommand()); + reg.registerCommand("clean-history", new CleanHistoryCommand(context)); } @Override diff -r b525881b4f8a -r 1925258e6309 client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/CleanHistoryCommand.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/CleanHistoryCommand.java Wed Jul 31 18:58:17 2013 -0400 @@ -0,0 +1,110 @@ +/* + * Copyright 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.cli.internal; + +import java.io.PrintStream; +import java.util.List; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; + +import com.redhat.thermostat.common.cli.AbstractCommand; +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.shared.locale.Translate; +import com.redhat.thermostat.storage.core.HostRef; +import com.redhat.thermostat.storage.core.Storage; +import com.redhat.thermostat.storage.dao.AgentInfoDAO; +import com.redhat.thermostat.storage.model.AgentInformation; + +public class CleanHistoryCommand extends AbstractCommand { + + private static final Translate translator = LocaleResources.createLocalizer(); + private BundleContext bundleContext; + + CleanHistoryCommand (BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + @Override + public void run(CommandContext ctx) throws CommandException { + ServiceReference storageServiceRef = bundleContext.getServiceReference(Storage.class); + if (storageServiceRef == null) { + throw new CommandException(translator.localize(LocaleResources.STORAGE_UNAVAILABLE)); + } + Storage storage = (Storage) bundleContext.getService(storageServiceRef); + + try { + List agentIdList = ctx.getArguments().getNonOptionArguments(); + PrintStream output = ctx.getConsole().getOutput(); + removeDataForSpecifiedAgents(storage, agentIdList, output); + } finally { + bundleContext.ungetService(storageServiceRef); + } + } + + public void removeDataForSpecifiedAgents(Storage storage, List agentIdList, PrintStream output) throws CommandException { + ServiceReference agentServiceRef = bundleContext.getServiceReference(AgentInfoDAO.class); + if (agentServiceRef == null) { + throw new CommandException(translator.localize(LocaleResources.AGENT_UNAVAILABLE)); + } + AgentInfoDAO agentInfoDAO = (AgentInfoDAO) bundleContext.getService(agentServiceRef); + + try { + for (String agentId : agentIdList) { + AgentInformation agentInfo = agentInfoDAO.getAgentInformation(new HostRef(agentId, agentId)); + if (agentInfo != null) { + removeDataIfAgentDead(storage, agentId, agentInfo, output); + } else { + output.println(translator.localize(LocaleResources.AGENT_NOT_FOUND, agentId).getContents()); + } + } + } finally { + bundleContext.ungetService(agentServiceRef); + } + } + + private void removeDataIfAgentDead(Storage storage, String agentId, AgentInformation agentInfo, PrintStream output) { + if (!agentInfo.isAlive()) { + output.println(translator.localize(LocaleResources.PURGING_AGENT_DATA).getContents() + agentId); + storage.purge(agentId); + } else { + output.println(translator.localize(LocaleResources.CANNOT_PURGE_AGENT_RUNNING, agentId).getContents()); + } + } + +} diff -r b525881b4f8a -r 1925258e6309 client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java --- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java Wed Jul 31 15:53:15 2013 -0400 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java Wed Jul 31 18:58:17 2013 -0400 @@ -86,6 +86,12 @@ HOSTID_REQUIRED_MESSAGE, VMID_REQUIRED_MESSAGE, + + PURGING_AGENT_DATA, + STORAGE_UNAVAILABLE, + AGENT_UNAVAILABLE, + CANNOT_PURGE_AGENT_RUNNING, + AGENT_NOT_FOUND, ; static final String RESOURCE_BUNDLE = "com.redhat.thermostat.client.cli.strings"; diff -r b525881b4f8a -r 1925258e6309 client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties --- a/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties Wed Jul 31 15:53:15 2013 -0400 +++ b/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties Wed Jul 31 18:58:17 2013 -0400 @@ -44,3 +44,9 @@ HOSTID_REQUIRED_MESSAGE = a hostId is required VMID_REQUIRED_MESSAGE = a vmId is required + +PURGING_AGENT_DATA = Purging data for agent: +STORAGE_UNAVAILABLE = Storage is unavailable +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! diff -r b525881b4f8a -r 1925258e6309 client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/CleanHistoryCommandTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/CleanHistoryCommandTest.java Wed Jul 31 18:58:17 2013 -0400 @@ -0,0 +1,197 @@ +/* + * Copyright 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.client.cli.internal; + +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.times; + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.redhat.thermostat.common.cli.Arguments; +import com.redhat.thermostat.common.cli.CommandContext; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.cli.Console; +import com.redhat.thermostat.storage.core.HostRef; +import com.redhat.thermostat.storage.core.Storage; +import com.redhat.thermostat.storage.dao.AgentInfoDAO; +import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.testutils.StubBundleContext; + +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(AgentInformation.class) +public class CleanHistoryCommandTest { + + private CleanHistoryCommand cleanHistoryCommand; + private CommandContext mockCommandContext; + private List agentIdList; + private Storage mockStorage; + private PrintStream mockOutput; + private AgentInfoDAO mockAgentInfoDAO; + + @Before + public void setUp() { + StubBundleContext bundleContext = new StubBundleContext(); + cleanHistoryCommand = new CleanHistoryCommand(bundleContext); + + mockStorage = mock(Storage.class); + bundleContext.registerService(Storage.class, mockStorage, null); + mockAgentInfoDAO = mock(AgentInfoDAO.class); + bundleContext.registerService(AgentInfoDAO.class, mockAgentInfoDAO, null); + + mockCommandContext = mock(CommandContext.class); + Arguments mockArguments = mock(Arguments.class); + + when(mockCommandContext.getArguments()).thenReturn(mockArguments); + when(mockArguments.getNonOptionArguments()).thenReturn(getArgumentList()); + + Console mockConsole = mock(Console.class); + mockOutput = mock(PrintStream.class); + when(mockCommandContext.getConsole()).thenReturn(mockConsole); + when(mockConsole.getOutput()).thenReturn(mockOutput); + } + + @Test + public void testOneValidArgument() throws CommandException { + AgentInformation mockAgent = PowerMockito.mock(AgentInformation.class); + when(mockAgent.getAgentId()).thenReturn("agentId1"); + when(mockAgent.isAlive()).thenReturn(false); + + when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId1", "agentId1"))).thenReturn((mockAgent)); + + cleanHistoryCommand.run(mockCommandContext); + + verify(mockStorage, times(1)).purge(isA(String.class)); + verify(mockStorage).purge("agentId1"); + verify(mockOutput).println("Purging data for agent: agentId1"); + } + + @Test + public void testMultipleValidArguments() throws CommandException { + AgentInformation mockAgent1 = PowerMockito.mock(AgentInformation.class); + AgentInformation mockAgent2 = PowerMockito.mock(AgentInformation.class); + AgentInformation mockAgent3 = PowerMockito.mock(AgentInformation.class); + when(mockAgent1.getAgentId()).thenReturn("agentId1"); + when(mockAgent2.getAgentId()).thenReturn("agentId2"); + when(mockAgent3.getAgentId()).thenReturn("agentId3"); + + when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId1", "agentId1"))).thenReturn((mockAgent1)); + when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId2", "agentId2"))).thenReturn((mockAgent2)); + when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId3", "agentId3"))).thenReturn((mockAgent3)); + + cleanHistoryCommand.run(mockCommandContext); + + verify(mockStorage, times(3)).purge(isA(String.class)); + verify(mockStorage).purge("agentId1"); + verify(mockStorage).purge("agentId2"); + verify(mockStorage).purge("agentId3"); + + verify(mockOutput, times(3)).println(isA(String.class)); + verify(mockOutput).println("Purging data for agent: agentId1"); + verify(mockOutput).println("Purging data for agent: agentId2"); + verify(mockOutput).println("Purging data for agent: agentId3"); + } + + @Test + public void testLiveAgents() throws CommandException { + AgentInformation mockAgent1 = PowerMockito.mock(AgentInformation.class); + AgentInformation mockAgent2 = PowerMockito.mock(AgentInformation.class); + AgentInformation mockAgent3 = PowerMockito.mock(AgentInformation.class); + when(mockAgent1.getAgentId()).thenReturn("agentId1"); + when(mockAgent2.getAgentId()).thenReturn("agentId2"); + when(mockAgent3.getAgentId()).thenReturn("agentId3"); + when(mockAgent1.isAlive()).thenReturn(true); + when(mockAgent2.isAlive()).thenReturn(false); + when(mockAgent3.isAlive()).thenReturn(true); + + when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId1", "agentId1"))).thenReturn((mockAgent1)); + when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId2", "agentId2"))).thenReturn((mockAgent2)); + when(mockAgentInfoDAO.getAgentInformation(new HostRef("agentId3", "agentId3"))).thenReturn((mockAgent3)); + + cleanHistoryCommand.run(mockCommandContext); + + verify(mockStorage, times(1)).purge(isA(String.class)); + verify(mockStorage, times(1)).purge("agentId2"); + + verify(mockOutput, times(3)).println(isA(String.class)); + verify(mockOutput).println("Cannot purge data for agent agentId1. This agent is currently running"); + verify(mockOutput).println("Purging data for agent: agentId2"); + verify(mockOutput).println("Cannot purge data for agent agentId3. This agent is currently running"); + } + + @Test + public void testInvalidArguments() throws CommandException { + AgentInformation mockAgent1 = PowerMockito.mock(AgentInformation.class); + AgentInformation mockAgent2 = PowerMockito.mock(AgentInformation.class); + when(mockAgent1.getAgentId()).thenReturn("validAgent1-in-DB"); + when(mockAgent2.getAgentId()).thenReturn("validAgent2-in-DB"); + + when(mockAgentInfoDAO.getAgentInformation(new HostRef("validAgent1-in-DB", "validAgent1-in-DB"))).thenReturn((mockAgent1)); + when(mockAgentInfoDAO.getAgentInformation(new HostRef("validAgent2-in-DB", "validAgent2-in-DB"))).thenReturn((mockAgent2)); + + cleanHistoryCommand.run(mockCommandContext); + + verify(mockStorage, never()).purge(isA(String.class)); + + verify(mockOutput, times(3)).println(isA(String.class)); + verify(mockOutput).println("Agent with an id agentId1 was not found!"); + verify(mockOutput).println("Agent with an id agentId2 was not found!"); + verify(mockOutput).println("Agent with an id agentId3 was not found!"); + } + + private List getArgumentList() { + agentIdList = new ArrayList(); + agentIdList.add("agentId1"); + agentIdList.add("agentId2"); + agentIdList.add("agentId3"); + return agentIdList; + } + +} diff -r b525881b4f8a -r 1925258e6309 distribution/config/commands/clean-history.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/distribution/config/commands/clean-history.properties Wed Jul 31 18:58:17 2013 -0400 @@ -0,0 +1,21 @@ +bundles = thermostat-client-cli-${project.version}.jar, \ + thermostat-storage-mongodb-${project.version}.jar, \ + thermostat-web-common-${project.version}.jar, \ + thermostat-web-client-${project.version}.jar, \ + httpcomponents-core.jar, \ + httpcomponents-client.jar, \ + gson.jar, \ + mongo.jar, \ + commons-beanutils.jar, \ + commons-codec.jar, \ + commons-collections.jar, \ + commons-logging.jar, \ + +description = Drop all data related to all of the specified agents + +usage = clean-history + +# This command does not have any options +options = AUTO_LOG_OPTION + +environments = cli, shell diff -r b525881b4f8a -r 1925258e6309 storage/core/src/main/java/com/redhat/thermostat/storage/core/Storage.java --- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/Storage.java Wed Jul 31 15:53:15 2013 -0400 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/Storage.java Wed Jul 31 18:58:17 2013 -0400 @@ -86,7 +86,7 @@ void removePojo(Remove remove); /** - * Drop all data related to the currently running agent. + * Drop all data related to the specified agent. */ void purge(String agentId); diff -r b525881b4f8a -r 1925258e6309 storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorage.java --- a/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorage.java Wed Jul 31 15:53:15 2013 -0400 +++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorage.java Wed Jul 31 18:58:17 2013 -0400 @@ -222,7 +222,8 @@ @Override public void purge(String agentId) { BasicDBObject query = new BasicDBObject(Key.AGENT_ID.getName(), agentId); - for (DBCollection coll : collectionCache.values()) { + for (String collectionName : db.getCollectionNames()) { + DBCollection coll = db.getCollectionFromString(collectionName); coll.remove(query); } } diff -r b525881b4f8a -r 1925258e6309 storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorageTest.java --- a/storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorageTest.java Wed Jul 31 15:53:15 2013 -0400 +++ b/storage/mongo/src/test/java/com/redhat/thermostat/storage/mongodb/internal/MongoStorageTest.java Wed Jul 31 18:58:17 2013 -0400 @@ -49,10 +49,13 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.times; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.lang.reflect.Field; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.UUID; import org.junit.After; @@ -203,6 +206,13 @@ when(db.createCollection(anyString(), any(DBObject.class))).thenReturn(testCollection); factory = new ExpressionFactory(); + + Set collectionNames = new LinkedHashSet<>(); + collectionNames.add("testCollection"); + collectionNames.add("emptyTestCollection"); + when(db.getCollectionNames()).thenReturn(collectionNames); + when(db.getCollectionFromString("testCollection")).thenReturn(testCollection); + when(db.getCollectionFromString("emptyTestCollection")).thenReturn(emptyTestCollection); } @After @@ -490,6 +500,18 @@ verify(mockMongo).close(); } + @Test + public void verifyDBPurge() throws Exception { + MongoStorage storage = makeStorage(); + setDbFieldInStorage(storage); + String agentId = "agentId123"; + BasicDBObject query = new BasicDBObject(Key.AGENT_ID.getName(), agentId); + storage.purge(agentId); + + verify(testCollection, times(1)).remove(query); + verify(emptyTestCollection, times(1)).remove(query); + } + private void setDbFieldInStorage(MongoStorage storage) throws Exception { // use a bit of reflection to set the db field Field dbField = storage.getClass().getDeclaredField("db");