/*
* RHQ Management Platform
* Copyright (C) 2005-2014 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.core.pc.bundle;
import java.io.File;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.rhq.core.clientapi.agent.PluginContainerException;
import org.rhq.core.clientapi.agent.bundle.BundleScheduleRequest;
import org.rhq.core.clientapi.agent.bundle.BundleScheduleResponse;
import org.rhq.core.clientapi.server.bundle.BundleServerService;
import org.rhq.core.domain.bundle.Bundle;
import org.rhq.core.domain.bundle.BundleDeployment;
import org.rhq.core.domain.bundle.BundleDeploymentStatus;
import org.rhq.core.domain.bundle.BundleDestination;
import org.rhq.core.domain.bundle.BundleResourceDeployment;
import org.rhq.core.domain.bundle.BundleResourceDeploymentHistory;
import org.rhq.core.domain.bundle.BundleType;
import org.rhq.core.domain.bundle.BundleVersion;
import org.rhq.core.domain.bundle.ResourceTypeBundleConfiguration;
import org.rhq.core.domain.bundle.ResourceTypeBundleConfiguration.BundleDestinationBaseDirectory.Context;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.content.PackageVersion;
import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.MeasurementData;
import org.rhq.core.domain.measurement.MeasurementDataTrait;
import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.measurement.MeasurementSchedule;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceCategory;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pc.PluginContainerConfiguration;
import org.rhq.core.pc.ServerServices;
import org.rhq.core.pc.inventory.InventoryManager;
import org.rhq.core.pc.inventory.ResourceContainer;
import org.rhq.core.pc.measurement.MeasurementManager;
import org.rhq.core.pc.plugin.PluginLifecycleListenerManager;
import org.rhq.core.pc.plugin.PluginLifecycleListenerManagerImpl;
import org.rhq.core.pc.plugin.PluginManager;
import org.rhq.core.pluginapi.bundle.BundleDeployRequest;
import org.rhq.core.pluginapi.bundle.BundleDeployResult;
import org.rhq.core.pluginapi.bundle.BundleFacet;
import org.rhq.core.pluginapi.bundle.BundlePurgeRequest;
import org.rhq.core.pluginapi.bundle.BundlePurgeResult;
import org.rhq.core.pluginapi.inventory.ResourceContext;
@Test
public class BundleManagerTest {
private MockBundleManager mockBundleManager;
private MockInventoryManager im;
private PluginContainerConfiguration pcConfig;
private PluginManager pluginManager;
private static final String BUNDLE_CONFIG_CONTEXT_VALUE_FS = getPath("/blah");
private static final String BUNDLE_CONFIG_LOCATION_PC = getPath("/pluginconfig/base/dir");
private static final String BUNDLE_CONFIG_LOCATION_RC = getPath("/resourceconfig/base/dir");
private static final String BUNDLE_CONFIG_LOCATION_MT = getPath("/trait/base/dir");
@BeforeMethod
public void beforeMethod() {
ServerServices serverServices = new ServerServices();
serverServices.setBundleServerService(new MockBundleServerService());
PluginLifecycleListenerManager pluginLifecycleListenerManager = new PluginLifecycleListenerManagerImpl();
pcConfig = new PluginContainerConfiguration();
pcConfig.setStartManagementBean(false);
pcConfig.setServerServices(serverServices);
ResourceContainer.initialize(pcConfig);
pluginManager = new PluginManager(pcConfig, pluginLifecycleListenerManager);
im = new MockInventoryManager();
im.initialize();
mockBundleManager = new MockBundleManager();
// clear any past interrupted state
Thread.interrupted();
}
@AfterMethod
public void afterMethod() {
mockBundleManager.shutdown();
im.shutdown();
im.getMeasurementManager().shutdown();
ResourceContainer.shutdown();
pcConfig = null;
}
public void testNonPlatformBundleDeploy_FileSystem_AbsolutePath() throws Exception {
BundleType bundleType = new BundleType("bundleTypeName", im.bundleHandlerType);
Bundle bundle = new Bundle("bundleName", bundleType, null, null);
BundleVersion bundleVersion = new BundleVersion("bundleVersionName", "1.0", bundle, "");
BundleDestination destination = new BundleDestination(bundle, "destName", null,
MockInventoryManager.BUNDLE_CONFIG_NAME_FS, getPath("/tmp/dest")); // ABSOLUTE PATH
BundleDeployment bundleDeployment = new BundleDeployment(bundleVersion, destination, "deploymentName");
BundleResourceDeployment resourceDeployment = new BundleResourceDeployment(bundleDeployment, im.serverFS);
BundleScheduleRequest request = new BundleScheduleRequest(resourceDeployment);
// No matter what the CONTEXT_VALUE_FS is (i.e. the default context value in the plugin descriptor),
// if the user specifies an absolute path for the destination, that will be used explicitly. So here in this test,
// the destination was specified with a destDir that had an absolute path of /tmp/dest and it will be used as-is
mockBundleManager.absolutePathToAssert = getPath("/tmp/dest");
BundleScheduleResponse response = mockBundleManager.schedule(request);
assertSuccess(response);
assertBundleDeploymentStatus(BundleDeploymentStatus.SUCCESS);
}
private static String getPath(String path) {
return ((File.separatorChar == '\\') && path.startsWith("/")) ? ("C:" + path) : path;
}
public void testNonPlatformBundleDeploy_FileSystem_RelativePath() throws Exception {
BundleType bundleType = new BundleType("bundleTypeName", im.bundleHandlerType);
Bundle bundle = new Bundle("bundleName", bundleType, null, null);
BundleVersion bundleVersion = new BundleVersion("bundleVersionName", "1.0", bundle, "");
BundleDestination destination = new BundleDestination(bundle, "destName", null,
MockInventoryManager.BUNDLE_CONFIG_NAME_FS, "relative/path"); // RELATIVE PATH
BundleDeployment bundleDeployment = new BundleDeployment(bundleVersion, destination, "deploymentName");
BundleResourceDeployment resourceDeployment = new BundleResourceDeployment(bundleDeployment, im.serverFS);
BundleScheduleRequest request = new BundleScheduleRequest(resourceDeployment);
// in the real world, the context value for fileSystem contexts will probably always be "/" but
// to test that we are really using this context value, our tests set it to something other than "/".
// That's why we prepend CONTEXT_VALUE_FS to the front of the destination's destDir
// note that we expect that relative path converted to absolute
mockBundleManager.absolutePathToAssert = BUNDLE_CONFIG_CONTEXT_VALUE_FS + "/relative/path";
BundleScheduleResponse response = mockBundleManager.schedule(request);
assertSuccess(response);
assertBundleDeploymentStatus(BundleDeploymentStatus.SUCCESS);
}
public void testNonPlatformBundleDeploy_PluginConfig() throws Exception {
BundleType bundleType = new BundleType("bundleTypeName", im.bundleHandlerType);
Bundle bundle = new Bundle("bundleName", bundleType, null, null);
BundleVersion bundleVersion = new BundleVersion("bundleVersionName", "1.0", bundle, "");
BundleDestination destination = new BundleDestination(bundle, "destName", null,
MockInventoryManager.BUNDLE_CONFIG_NAME_PC, "relative/path/pc");
BundleDeployment bundleDeployment = new BundleDeployment(bundleVersion, destination, "deploymentName");
BundleResourceDeployment resourceDeployment = new BundleResourceDeployment(bundleDeployment, im.serverPC);
BundleScheduleRequest request = new BundleScheduleRequest(resourceDeployment);
mockBundleManager.absolutePathToAssert = BUNDLE_CONFIG_LOCATION_PC + "/relative/path/pc";
BundleScheduleResponse response = mockBundleManager.schedule(request);
assertSuccess(response);
assertBundleDeploymentStatus(BundleDeploymentStatus.SUCCESS);
}
public void testNonPlatformBundleDeploy_ResourceConfig() throws Exception {
BundleType bundleType = new BundleType("bundleTypeName", im.bundleHandlerType);
Bundle bundle = new Bundle("bundleName", bundleType, null, null);
BundleVersion bundleVersion = new BundleVersion("bundleVersionName", "1.0", bundle, "");
BundleDestination destination = new BundleDestination(bundle, "destName", null,
MockInventoryManager.BUNDLE_CONFIG_NAME_RC, "relative/path/rc");
BundleDeployment bundleDeployment = new BundleDeployment(bundleVersion, destination, "deploymentName");
BundleResourceDeployment resourceDeployment = new BundleResourceDeployment(bundleDeployment, im.serverRC);
BundleScheduleRequest request = new BundleScheduleRequest(resourceDeployment);
mockBundleManager.absolutePathToAssert = BUNDLE_CONFIG_LOCATION_RC + "/relative/path/rc";
BundleScheduleResponse response = mockBundleManager.schedule(request);
assertSuccess(response);
assertBundleDeploymentStatus(BundleDeploymentStatus.SUCCESS);
}
public void testNonPlatformBundleDeploy_Trait() throws Exception {
BundleType bundleType = new BundleType("bundleTypeName", im.bundleHandlerType);
Bundle bundle = new Bundle("bundleName", bundleType, null, null);
BundleVersion bundleVersion = new BundleVersion("bundleVersionName", "1.0", bundle, "");
BundleDestination destination = new BundleDestination(bundle, "destName", null,
MockInventoryManager.BUNDLE_CONFIG_NAME_MT, "relative/path/mt");
BundleDeployment bundleDeployment = new BundleDeployment(bundleVersion, destination, "deploymentName");
BundleResourceDeployment resourceDeployment = new BundleResourceDeployment(bundleDeployment, im.serverMT);
BundleScheduleRequest request = new BundleScheduleRequest(resourceDeployment);
mockBundleManager.absolutePathToAssert = BUNDLE_CONFIG_LOCATION_MT + "/relative/path/mt";
BundleScheduleResponse response = mockBundleManager.schedule(request);
assertSuccess(response);
assertBundleDeploymentStatus(BundleDeploymentStatus.SUCCESS);
}
public void testNonPlatformBundleDeploy_FileSystem_Failure() throws Exception {
BundleType bundleType = new BundleType("bundleTypeName", im.bundleHandlerType);
Bundle bundle = new Bundle("bundleName", bundleType, null, null);
BundleVersion bundleVersion = new BundleVersion("bundleVersionName", "1.0", bundle, "");
BundleDestination destination = new BundleDestination(bundle, "destName", null,
MockInventoryManager.BUNDLE_CONFIG_NAME_FS, getPath("/tmp/dest")); // ABSOLUTE PATH
BundleDeployment bundleDeployment = new BundleDeployment(bundleVersion, destination, "deploymentName");
BundleResourceDeployment resourceDeployment = new BundleResourceDeployment(bundleDeployment, im.serverFS);
BundleScheduleRequest request = new BundleScheduleRequest(resourceDeployment);
mockBundleManager.absolutePathToAssert = "/should_fail_to_match"; // this will not match the /tmp/dest that we set the destination to
BundleScheduleResponse response = mockBundleManager.schedule(request);
assertSuccess(response);
assertBundleDeploymentStatus(BundleDeploymentStatus.FAILURE);
}
private void assertSuccess(BundleScheduleResponse response) {
assert response.isSuccess() : response;
}
private void assertBundleDeploymentStatus(BundleDeploymentStatus statusToAssert) throws Exception {
MockBundleServerService bundleService;
bundleService = (MockBundleServerService) pcConfig.getServerServices().getBundleServerService();
assert bundleService.lastStatusLatch.await(30, TimeUnit.SECONDS) : "Test did not complete in a timely manner - is it hung?";
assert bundleService.lastStatus == statusToAssert : "Deployment status [" + bundleService.lastStatus
+ "] did not match what was expected [" + statusToAssert + "]";
}
private class MockBundleManager extends BundleManager {
public MockBundleManager() {
super(pcConfig, new MockAgentServiceStreamRemoter(), im, new MockMeasurementManager(pcConfig));
}
public String absolutePathToAssert;
@Override
protected BundleFacet getBundleFacet(int resourceId, long timeout) throws PluginContainerException {
return new MockBundleFacet(this);
}
}
private class MockBundleFacet implements BundleFacet {
MockBundleManager manager;
public MockBundleFacet(MockBundleManager mbm) {
manager = mbm;
}
@Override
public BundleDeployResult deployBundle(BundleDeployRequest request) {
BundleDeployResult result = new BundleDeployResult();
// tests should be setting MockBundleManager.absolutePathToAssert to the path that should be expected
if (!request.getAbsoluteDestinationDirectory().equals(new File(manager.absolutePathToAssert))) {
result.setErrorMessage("absolute path [" + request.getAbsoluteDestinationDirectory()
+ "] did not match the expected path [" + manager.absolutePathToAssert + "]");
System.out.println(result.getErrorMessage());
}
return result;
}
@Override
public BundlePurgeResult purgeBundle(BundlePurgeRequest request) {
return new BundlePurgeResult();
}
}
private class MockBundleServerService implements BundleServerService {
public BundleDeploymentStatus lastStatus = null;
public CountDownLatch lastStatusLatch = new CountDownLatch(1);
@Override
public void addDeploymentHistory(int bundleDeploymentId, BundleResourceDeploymentHistory history) {
return;
}
@Override
public long downloadPackageBits(PackageVersion packageVersion, OutputStream outputStream) {
return 0;
}
@Override
public List<PackageVersion> getAllBundleVersionPackageVersions(int bundleVersionId) {
return new ArrayList<PackageVersion>(0);
}
@Override
public void setBundleDeploymentStatus(int bundleDeploymentId, BundleDeploymentStatus status) {
// only track success or failure status
if ((status == BundleDeploymentStatus.SUCCESS) || (status == BundleDeploymentStatus.FAILURE)) {
lastStatus = status;
lastStatusLatch.countDown();
}
return;
}
}
private class MockInventoryManager extends InventoryManager {
private static final String BUNDLE_CONFIG_NAME_FS = "fsBaseDirLocation";
private static final String BUNDLE_CONFIG_NAME_PC = "pcBaseDirLocation";
private static final String BUNDLE_CONFIG_CONTEXT_VALUE_PC = "pcPropBundle";
private static final String BUNDLE_CONFIG_NAME_RC = "rcBaseDirLocation";
private static final String BUNDLE_CONFIG_CONTEXT_VALUE_RC = "rcPropBundle";
private static final String BUNDLE_CONFIG_NAME_MT = "mtBaseDirLocation";
private static final String BUNDLE_CONFIG_CONTEXT_VALUE_MT = "traitBundle";
// mocking the following:
// - one platform type and a platform resource to be used as the root parent
// - one bundle handler type and resource to mimic our bundle handler component
// - one server resource for each kind of "destination base directory context"
// ** FS = fileSystem (the bundle will be deployed directly to the root file system)
// ** PC = pluginConfiguration (bundle deployed to a directory specified in a plugin config property)
// ** RC = resourceConfiguration (bundle deployed to a directory specified in a resource config property)
// ** MT = measurementTrait (bundle deployed to a directory specified in a measurement trait value)
public ResourceType platformType;
public ResourceType bundleHandlerType;
public ResourceType serverTypeFS;
public ResourceType serverTypePC;
public ResourceType serverTypeRC;
public ResourceType serverTypeMT;
public Resource platform;
public Resource bundleHandler;
public Resource serverFS;
public Resource serverPC;
public Resource serverRC;
public Resource serverMT;
public final HashMap<ResourceType, Resource> typeResourceMap = new HashMap<ResourceType, Resource>();
public final HashMap<Integer, ResourceContainer> idResourceContainerMap = new HashMap<Integer, ResourceContainer>();
public MockInventoryManager() {
super(pcConfig, null, pluginManager);
platformType = new ResourceType("platformResourceTypeName", "pluginName", ResourceCategory.PLATFORM, null);
bundleHandlerType = new ResourceType("bhRTypeName", "pluginName", ResourceCategory.SERVER, platformType);
serverTypeFS = new ResourceType("typeName-fileSystem", "pluginName", ResourceCategory.SERVER, platformType);
serverTypePC = new ResourceType("typeName-plugConfig", "pluginName", ResourceCategory.SERVER, platformType);
serverTypeRC = new ResourceType("typeName-reSconfig", "pluginName", ResourceCategory.SERVER, platformType);
serverTypeMT = new ResourceType("typeName-trait", "pluginName", ResourceCategory.SERVER, platformType);
int id = 1;
platform = new Resource("platformKey", "platformName", platformType);
platform.setId(id++);
bundleHandler = new Resource("bhKey", "bhName", bundleHandlerType);
bundleHandler.setId(id++);
bundleHandler.setParentResource(platform);
bundleHandler.setUuid(UUID.randomUUID().toString());
serverFS = new Resource("serverKey-fileSystem", "serverName-fileSystem", serverTypeFS);
serverFS.setId(id++);
serverFS.setParentResource(platform);
serverPC = new Resource("serverKey-plugConfig", "serverName-plugConfig", serverTypePC);
serverPC.setId(id++);
serverPC.setParentResource(platform);
serverRC = new Resource("serverKey-resConfig", "serverName-resConfig", serverTypeRC);
serverRC.setId(id++);
serverRC.setParentResource(platform);
serverMT = new Resource("serverKey-traitConfig", "serverName-traitConfig", serverTypeMT);
serverMT.setId(id++);
serverMT.setParentResource(platform);
typeResourceMap.put(platformType, platform);
typeResourceMap.put(bundleHandlerType, bundleHandler);
typeResourceMap.put(serverTypeFS, serverFS);
typeResourceMap.put(serverTypePC, serverPC);
typeResourceMap.put(serverTypeRC, serverRC);
typeResourceMap.put(serverTypeMT, serverMT);
ResourceContainer platformContainer = new ResourceContainer(platform, null);
ResourceContainer bundleHandlerContainer = new ResourceContainer(bundleHandler, null);
ResourceContainer serverContainerFS = new ResourceContainer(serverFS, null);
ResourceContainer serverContainerPC = new ResourceContainer(serverPC, null);
ResourceContainer serverContainerRC = new ResourceContainer(serverRC, null);
ResourceContainer serverContainerMT = new ResourceContainer(serverMT, null);
idResourceContainerMap.put(platform.getId(), platformContainer);
idResourceContainerMap.put(bundleHandler.getId(), bundleHandlerContainer);
idResourceContainerMap.put(serverFS.getId(), serverContainerFS);
idResourceContainerMap.put(serverPC.getId(), serverContainerPC);
idResourceContainerMap.put(serverRC.getId(), serverContainerRC);
idResourceContainerMap.put(serverMT.getId(), serverContainerMT);
bundleHandlerContainer.setResourceContext(new MockResourceContext(bundleHandler));
// each different resource type that supports bundle deployments needs to define its
// bundle configuration to denote where the base directory location is found.
// Today we support four ways: via plugin config property, resource config property,
// measurement trait value, or strictly on the root file system (using no resource specific value)
ResourceTypeBundleConfiguration rtbc = new ResourceTypeBundleConfiguration(new Configuration());
rtbc.addBundleDestinationBaseDirectory(BUNDLE_CONFIG_NAME_FS, Context.fileSystem.name(),
BUNDLE_CONFIG_CONTEXT_VALUE_FS, null);
serverTypeFS.setResourceTypeBundleConfiguration(rtbc);
rtbc = new ResourceTypeBundleConfiguration(new Configuration());
rtbc.addBundleDestinationBaseDirectory(BUNDLE_CONFIG_NAME_PC, Context.pluginConfiguration.name(),
BUNDLE_CONFIG_CONTEXT_VALUE_PC, null);
serverTypePC.setResourceTypeBundleConfiguration(rtbc);
rtbc = new ResourceTypeBundleConfiguration(new Configuration());
rtbc.addBundleDestinationBaseDirectory(BUNDLE_CONFIG_NAME_RC, Context.resourceConfiguration.name(),
BUNDLE_CONFIG_CONTEXT_VALUE_RC, null);
serverTypeRC.setResourceTypeBundleConfiguration(rtbc);
rtbc = new ResourceTypeBundleConfiguration(new Configuration());
rtbc.addBundleDestinationBaseDirectory(BUNDLE_CONFIG_NAME_MT, Context.measurementTrait.name(),
BUNDLE_CONFIG_CONTEXT_VALUE_MT, null);
serverTypeMT.setResourceTypeBundleConfiguration(rtbc);
// each different resource needs to specify where exactly it wants the bundles deployed
// using the different contexts that are supported.
Configuration pluginConfiguration = new Configuration();
pluginConfiguration.put(new PropertySimple(BUNDLE_CONFIG_CONTEXT_VALUE_PC, BUNDLE_CONFIG_LOCATION_PC));
serverPC.setPluginConfiguration(pluginConfiguration);
Configuration resourceConfiguration = new Configuration();
resourceConfiguration.put(new PropertySimple(BUNDLE_CONFIG_CONTEXT_VALUE_RC, BUNDLE_CONFIG_LOCATION_RC));
serverRC.setResourceConfiguration(resourceConfiguration);
MeasurementDefinition definition = new MeasurementDefinition(serverTypeMT, BUNDLE_CONFIG_CONTEXT_VALUE_MT);
definition.setDataType(DataType.TRAIT);
definition.setId(123);
MeasurementSchedule schedule = new MeasurementSchedule(definition, serverMT);
schedule.setId(123123);
MeasurementScheduleRequest scheduleRequest = new MeasurementScheduleRequest(schedule);
Set<MeasurementScheduleRequest> schedules = new HashSet<MeasurementScheduleRequest>(1);
schedules.add(scheduleRequest);
serverContainerMT.setMeasurementSchedule(schedules);
}
@Override
public Set<Resource> getResourcesWithType(ResourceType type) {
HashSet<Resource> set = new HashSet<Resource>(1);
set.add(typeResourceMap.get(type));
return set;
}
@Override
public ResourceContainer getResourceContainer(int resourceId) {
return idResourceContainerMap.get(resourceId);
}
@Override
public ResourceContainer getResourceContainer(Resource resource) {
if (idResourceContainerMap == null)
return null;
return idResourceContainerMap.get(resource.getId());
}
}
private class MockMeasurementManager extends MeasurementManager {
public MockMeasurementManager(PluginContainerConfiguration configuration) {
super(configuration, null, im);
}
@Override
public Set<MeasurementData> getRealTimeMeasurementValue(int resourceId, Set<MeasurementScheduleRequest> requests) {
// anytime this method gets called, it means our tests are asking for the test trait value. It will
// always be the same value for all tests.
MeasurementDataTrait data = new MeasurementDataTrait(requests.iterator().next(), BUNDLE_CONFIG_LOCATION_MT);
Set<MeasurementData> values = new HashSet<MeasurementData>();
values.add(data);
return values;
}
}
@SuppressWarnings("unchecked")
private static class MockResourceContext extends ResourceContext {
public MockResourceContext(Resource resource) {
super(resource, null, null, null, null, null, null, null, null, null, null, null, null, null);
}
}
}