/* * RHQ Management Platform * Copyright (C) 2005-2012 Red Hat, Inc. * All rights reserved. * * This program 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 version 2 of the License. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.enterprise.server.resource.metadata; import static java.util.Arrays.asList; import java.io.File; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.ejb.EJBException; import org.testng.annotations.Test; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.cloud.Server; import org.rhq.core.domain.criteria.ServerCriteria; import org.rhq.core.domain.plugin.Plugin; import org.rhq.core.domain.plugin.PluginStatusType; import org.rhq.core.domain.resource.Agent; import org.rhq.core.domain.resource.ResourceType; import org.rhq.core.util.file.FileUtil; import org.rhq.enterprise.server.agentclient.AgentClient; import org.rhq.enterprise.server.auth.SubjectManagerLocal; import org.rhq.enterprise.server.cloud.TopologyManagerLocal; import org.rhq.enterprise.server.core.AgentManagerLocal; import org.rhq.enterprise.server.inventory.InventoryManagerLocal; import org.rhq.enterprise.server.resource.ResourceTypeManagerLocal; import org.rhq.enterprise.server.scheduler.jobs.PurgePluginsJob; import org.rhq.enterprise.server.scheduler.jobs.PurgeResourceTypesJob; import org.rhq.enterprise.server.test.TestAgentClient; import org.rhq.enterprise.server.test.TestServerCommunicationsService; import org.rhq.enterprise.server.test.TransactionCallback; import org.rhq.enterprise.server.util.LookupUtil; import junit.framework.Assert; /** * Unit tests for {@link PluginManagerBean}. */ @Test(groups = { "plugin.metadata", "PluginManagerBean" }, priority = 100000) public class PluginManagerBeanTest extends MetadataBeanTest { private static final String PLUGIN_1 = "PluginManagerBeanTestPlugin1"; private static final String PLUGIN_2 = "PluginManagerBeanTestPlugin2"; private static final String PLUGIN_3 = "PluginManagerBeanTestPlugin3"; private static final String PLUGIN_3_1 = "PluginManagerBeanTestPlugin3.1"; private SubjectManagerLocal subjectMgr; private PluginManagerLocal pluginMgr; private AgentManagerLocal agentMgr; private TestServerCommunicationsService agentServiceContainer; private boolean updatePluginsCalled; private CountDownLatch pluginUpdateProgressWaiter; private CountDownLatch pluginUpdateFinishWaiter; private Agent agent; @Override protected void beforeMethod() throws Exception { super.beforeMethod(); subjectMgr = LookupUtil.getSubjectManager(); pluginMgr = LookupUtil.getPluginManager(); agentMgr = LookupUtil.getAgentManager(); TopologyManagerLocal topMgr = LookupUtil.getTopologyManager(); Server svr = topMgr.findServersByCriteria(subjectMgr.getOverlord(), new ServerCriteria()).get(0); agent = new Agent(); agent.setAddress("kachny"); agent.setAgentToken("1234"); agent.setName("kachny"); agent.setPort(1234); agent.setRemoteEndpoint("kachny"); agent.setServer(svr); agentMgr.createAgent(agent); preparePluginScannerService().startDeployment(); prepareScheduler(); pluginUpdateProgressWaiter = new CountDownLatch(1); updatePluginsCalled = false; agentServiceContainer = prepareForTestAgents(new TestServerCommunicationsService() { @Override public AgentClient getKnownAgentClient(Agent agent) { AgentClient ret = new TestAgentClient(agent, this) { @Override public void updatePlugins() { updatePluginsCalled = true; pluginUpdateProgressWaiter.countDown(); try { Thread.sleep(1000); } catch (InterruptedException e) { //ignored } finally { if (pluginUpdateFinishWaiter != null) { try { pluginUpdateFinishWaiter.await(); } catch (InterruptedException e) { //ignored } } } } }; agentClients.put(agent, ret); return ret; } }); } @Override protected void afterMethod() throws Exception { FileUtil.purge(new File(getPluginScannerService().getAgentPluginDir()), true); unpreparePluginScannerService(); unprepareScheduler(); agentMgr.deleteAgent(agent); super.afterMethod(); } public void registerPlugins() throws Exception { List<Plugin> plugins = getEntityManager() .createQuery( "from Plugin where name IN ('PluginManagerBeanTestPlugin1', 'PluginManagerBeanTestPlugin2', 'PluginManagerBeanTestPlugin3')") .getResultList(); if (!plugins.isEmpty()) { System.out.println("Purging plugins " + plugins + "..."); for (Plugin plugin : plugins) { pluginMgr.deletePlugins(subjectMgr.getOverlord(), asList(plugin.getId())); } new PurgeResourceTypesJob().execute(null); new PurgePluginsJob().execute(null); } createPluginJarFile("test-plugin1.jar", "plugin_1.xml"); createPluginJarFile("test-plugin2.jar", "plugin_2.xml"); createPluginJarFile("test-plugin3.jar", "plugin_3.xml"); createPluginJarFile("test-plugin3.1.jar", "plugin_3.1.xml"); getPluginScannerService().scanAndRegister(); // ignore some types to make sure the plugin manager can still work with ignored types ignoreType("TestServer1Ignored", PLUGIN_1); ignoreType("TestServer2Ignored", PLUGIN_2); ignoreType("TestServer3Ignored", PLUGIN_3); ignoreType("TestServer3.1Ignored", PLUGIN_3_1); return; } @Test(dependsOnMethods = { "registerPlugins" }) public void scanAndRegisterTest() throws Exception { Plugin plugin = getPlugin(PLUGIN_1); assertNotNull(plugin); pluginDeployed(PLUGIN_1); plugin = getPlugin(PLUGIN_2); assertNotNull(plugin); pluginDeployed(PLUGIN_2); plugin = getPlugin(PLUGIN_3); assertNotNull(plugin); pluginDeployed(PLUGIN_3); plugin = getPlugin(PLUGIN_3_1); assertNotNull(plugin); pluginDeployed(PLUGIN_3_1); } @Test(dependsOnMethods = { "registerPlugins" }) public void disablePlugin() throws Exception { Plugin plugin = getPlugin(PLUGIN_3); assertTrue("Plugin should not already be disabled", plugin.isEnabled()); pluginMgr.disablePlugins(subjectMgr.getOverlord(), asList(plugin.getId())); plugin = pluginMgr.getPlugin(PLUGIN_3); assertNotNull(plugin); assertFalse("Failed to disable plugin", plugin.isEnabled()); } @Test(dependsOnMethods = { "registerPlugins" }) public void doNotDisablePluginIfDependentPluginsAreNotAlsoDisabled() throws Exception { Plugin plugin = getPlugin(PLUGIN_1); assertTrue("Plugin should not already be disabled", plugin.isEnabled()); Plugin plugin2 = getPlugin(PLUGIN_2); assertTrue("Plugin should not already be disabled", plugin.isEnabled()); Exception exception = null; try { pluginMgr.disablePlugins(subjectMgr.getOverlord(), asList(plugin.getId())); } catch (Exception e) { exception = e; } plugin = getPlugin(PLUGIN_1); assertTrue("Plugin should not have been disabled", plugin.isEnabled()); assertNotNull("Expected exception to be thrown when trying to disable a plugin that has dependent plugins", exception); assertTrue("Expected an IllegalArgumentException when trying to disable a plugin with dependent plugins", exception.getCause() instanceof IllegalArgumentException); } @Test(dependsOnMethods = { "doNotDisablePluginIfDependentPluginsAreNotAlsoDisabled" }) public void disablePluginAndDependentPlugins() throws Exception { Plugin plugin1 = getPlugin(PLUGIN_1); Plugin plugin2 = getPlugin(PLUGIN_2); pluginMgr.disablePlugins(subjectMgr.getOverlord(), asList(plugin1.getId(), plugin2.getId())); plugin1 = getPlugin(PLUGIN_1); plugin2 = getPlugin(PLUGIN_2); assertFalse("Failed to disable plugin", plugin1.isEnabled()); assertFalse("Failed to disable plugin", plugin2.isEnabled()); } @Test(groups = { "plugin.metadata", "PluginManagerBean" }, dependsOnMethods = { "disablePluginAndDependentPlugins" }) public void enablePlugins() throws Exception { Plugin plugin1 = getPlugin(PLUGIN_1); Plugin plugin2 = getPlugin(PLUGIN_2); pluginMgr.enablePlugins(subjectMgr.getOverlord(), asList(plugin1.getId(), plugin2.getId())); plugin1 = getPlugin(PLUGIN_1); plugin2 = getPlugin(PLUGIN_2); assertTrue("Failed to enable plugin", plugin1.isEnabled()); assertTrue("Failed to enable plugin", plugin2.isEnabled()); } @Test(dependsOnMethods = { "enablePlugins" }) public void doNotDeletePluginIfDependentPluginIsNotAlsoDeleted() throws Exception { Plugin plugin = getPlugin(PLUGIN_1); Exception exception = null; try { pluginMgr.deletePlugins(subjectMgr.getOverlord(), asList(plugin.getId())); fail("Expected an IllegalArgumentException when trying to delete a plugin with dependent plugins"); } catch (IllegalArgumentException e) { // expected } catch (EJBException ee) { if (ee.getCause() == null || !(ee.getCause() instanceof IllegalArgumentException)) { fail("Expected an IllegalArgumentException when trying to delete a plugin with dependent plugins, got: " + ee); } } catch (Throwable t) { fail("Expected an IllegalArgumentException when trying to delete a plugin with dependent plugins, got: " + t); } } @Test(dependsOnMethods = { "doNotDeletePluginIfDependentPluginIsNotAlsoDeleted" }) public void deletePlugins() throws Exception { Plugin plugin1 = getPlugin(PLUGIN_1); Plugin plugin2 = getPlugin(PLUGIN_2); pluginMgr.deletePlugins(subjectMgr.getOverlord(), asList(plugin1.getId(), plugin2.getId())); plugin1 = getPlugin(PLUGIN_1, "Deleting a plugin should not remove it from the database"); plugin2 = getPlugin(PLUGIN_2, "Deleting a plugin should not remove it from the database"); assertTrue("Expected plugin status to be set to DELETED", plugin1.getStatus() == PluginStatusType.DELETED); assertTrue("Expected plugin status to be set to DELETED", plugin2.getStatus() == PluginStatusType.DELETED); } @Test(dependsOnMethods = { "deletePlugins" }) public void isPluginReadyForPurge() throws Exception { ResourceTypeManagerLocal resourceTypeManager = LookupUtil.getResourceTypeManager(); InventoryManagerLocal inventoryManager = LookupUtil.getInventoryManager(); Plugin plugin = getDeletedPlugin(PLUGIN_1); if (plugin == null) { //ok so there's no delete plugin like that. Let's check that there's no installed plugin either plugin = pluginMgr.getPlugin(PLUGIN_1); if (plugin != null) { fail(PLUGIN_1 + "should have been deleted in PluginManagerBeanTest#deletePlugins()"); } //So there's no such plugin at all. This means that some other test intertwined between this test and //deletePlugins. // //Because tests are configured to clean up after themselves (mainly in the afterClassWork() method) //it may happen that the plugin we marked for deletion in deletePlugins has actually been deleted //by the clean up methods. // //The point of this test is in that case fulfilled anyway because by the plugin disappearing, //we proved that at some point in time between deletePlugins and this test it was indeed purgeable. return; } List<ResourceType> resourceTypes = resourceTypeManager.getResourceTypesByPlugin(plugin.getName()); List<ResourceType> deletedTypes = inventoryManager.getDeletedTypes(); boolean resourceTypesPurged = resourceTypes.isEmpty() && deletedTypes.isEmpty(); //ack the plugins as deleted so that the only remaining condition for their //purge-ability is the disappearance of their resource types. ackDeletedPlugins(); assertTrue("A plugin is not ready to be purged until all of its resource types have already been purged " + "and until the plugin itself has been acked for deletion by all servers", resourceTypesPurged == pluginMgr.isReadyForPurge(plugin)); } private Plugin getDeletedPlugin(String name) { List<Plugin> deletedPlugins = pluginMgr.findAllDeletedPlugins(); for (Plugin plugin : deletedPlugins) { if (plugin.getName().equals(name)) { return plugin; } } return null; } @Test(dependsOnMethods = { "registerPlugins", "isPluginReadyForPurge" }) public void pluginPurgeCheckShouldUseExactMatchesInQuery() throws Exception { // See https://bugzilla.redhat.com/show_bug.cgi?id=845700 for details on this test InventoryManagerLocal inventoryManager = LookupUtil.getInventoryManager(); ResourceTypeManagerLocal resourceTypeManager = LookupUtil.getResourceTypeManager(); Plugin plugin3 = getPlugin(PLUGIN_3); ResourceType resourceType = resourceTypeManager .getResourceTypeByNameAndPlugin("TestServer3", plugin3.getName()); ResourceType resourceTypeIgnored = resourceTypeManager.getResourceTypeByNameAndPlugin("TestServer3Ignored", plugin3.getName()); assertNotNull("Failed to find resource type. Did the resource type name in the plugin descriptor change?", resourceType); assertNotNull("Failed to find ignored resource type. Did the type name in the plugin descriptor change?", resourceTypeIgnored); pluginMgr.deletePlugins(subjectMgr.getOverlord(), asList(plugin3.getId())); inventoryManager.purgeDeletedResourceType(resourceType); inventoryManager.purgeDeletedResourceType(resourceTypeIgnored); ackDeletedPlugins(); assertTrue("Expected " + plugin3 + " to be ready for purge since all its resource types have been purged " + "and the servers acked its deletion", pluginMgr.isReadyForPurge(plugin3)); } // this needs to be the last test executed in the class, it does cleanup @Test(priority = 10, alwaysRun = true, dependsOnMethods = { "pluginPurgeCheckShouldUseExactMatchesInQuery" }) public void afterClassWorkTest() throws Exception { afterClassWork(); } @Test public void testScheduleUpdateOnAgents() throws Exception { Subject overlord = subjectMgr.getOverlord(); pluginMgr.schedulePluginUpdateOnAgents(overlord, 0); pluginUpdateProgressWaiter.await(); Assert.assertTrue(updatePluginsCalled); } @Test public void testUpdateNotDoneUntilAgentReturns() throws Exception { pluginUpdateFinishWaiter = new CountDownLatch(1); Subject overlord = subjectMgr.getOverlord(); String handle = pluginMgr.schedulePluginUpdateOnAgents(overlord, 0); pluginUpdateProgressWaiter.await(); boolean finished = pluginMgr.isPluginUpdateOnAgentsFinished(subjectMgr.getOverlord(), handle); Assert.assertEquals(false, finished); pluginUpdateFinishWaiter.countDown(); Thread.sleep(5000); //wait just a bit so that the request can bubble from the fake agent to the database finished = pluginMgr.isPluginUpdateOnAgentsFinished(subjectMgr.getOverlord(), handle); Assert.assertEquals(true, finished); } private Plugin getPlugin(String name) { Plugin plugin = pluginMgr.getPlugin(name); assertNotNull("Failed to find plugin [" + name + "].", plugin); return plugin; } private Plugin getPlugin(String name, String msg) { List<Plugin> plugins = getEntityManager().createQuery("from Plugin where name = :name") .setParameter("name", name).getResultList(); assertTrue("Failed to find plugin [" + name + "]: " + msg, plugins.size() == 1); return plugins.get(0); } }