/*
* RHQ Management Platform
* Copyright (C) 2013 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.plugins.ant;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.persistence.Query;
import javax.transaction.TransactionManager;
import org.testng.annotations.Test;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.bundle.BundleType;
import org.rhq.core.domain.resource.ResourceCategory;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.util.file.FileUtil;
import org.rhq.core.util.stream.StreamUtil;
import org.rhq.enterprise.server.bundle.BundleManagerLocal;
import org.rhq.enterprise.server.core.CoreServer;
import org.rhq.enterprise.server.core.CoreServerMBean;
import org.rhq.enterprise.server.core.plugin.PluginDeploymentScanner;
import org.rhq.enterprise.server.core.plugin.PluginDeploymentScannerMBean;
import org.rhq.enterprise.server.plugin.pc.MasterServerPluginContainer;
import org.rhq.enterprise.server.plugin.pc.ServerPluginService;
import org.rhq.enterprise.server.test.AbstractEJB3Test;
import org.rhq.enterprise.server.test.TestAgentClient;
import org.rhq.enterprise.server.test.TestServerCommunicationsService;
import org.rhq.enterprise.server.util.LookupUtil;
/**
* @author Lukas Krejci
*/
public class RecipeValidationTest extends AbstractEJB3Test {
public static final String ITESTS = "itests";
private static final String ENTITY_NAME_PREFIX = "recipeValidationTest";
private static final String FAKE_RESOURCE_TYPE_NAME = "recipeValidationTest-antbundle-resourcetype";
private static final String ANT_BUNDLE_TYPE_NAME = "Ant Bundle";
private TestServerCommunicationsService agentServiceContainer;
private MasterServerPluginContainer pc;
private ServerPluginService ps;
private BundleManagerLocal bundleManager;
private File serverPluginsDir;
@Override
protected void beforeMethod() throws Exception {
// try and clean up any junk that may be lying around from a failed run
cleanupDatabase();
bundleManager = LookupUtil.getBundleManager();
createAntBundleType();
prepareCustomServerService(new CoreServer(), CoreServerMBean.OBJECT_NAME);
prepareCustomServerService(new PluginDeploymentScanner(), PluginDeploymentScannerMBean.OBJECT_NAME);
prepareCustomServerPluginService(new ServerPluginService());
agentServiceContainer = prepareForTestAgents();
agentServiceContainer.bundleService = new TestAgentClient(null, agentServiceContainer);
ps = new ServerPluginService();
serverPluginsDir = ps.getServerPluginsDirectory();
serverPluginsDir.mkdirs();
File agentPluginsDir = new File(serverPluginsDir.getParentFile(), "agentplugins");
agentPluginsDir.mkdirs();
File antBundlePlugin = new File(System.getProperty("rhq.ant-bundle.serverplugin.path"));
PluginDeploymentScannerMBean scanner = LookupUtil.getPluginDeploymentScanner();
//needed by server plugin lifecycle
prepareScheduler();
File targetFile = new File(serverPluginsDir, antBundlePlugin.getName());
//touch the file so that the plugin scanner picks it up again
targetFile.setLastModified(System.currentTimeMillis());
FileUtil.copyFile(antBundlePlugin, targetFile);
scanner.setAgentPluginDir(agentPluginsDir.getAbsolutePath());
scanner.setServerPluginDir(serverPluginsDir.getAbsolutePath());
//actually, this is resetting the plugin service to the real thing, because we need to deploy the
//real ant bundle server plugin
prepareCustomServerPluginService(ps);
// resourceManager = LookupUtil.getResourceManager();
// overlord = c
ps.startMasterPluginContainer();
LookupUtil.getPluginDeploymentScanner().startDeployment();
LookupUtil.getPluginDeploymentScanner().scanAndRegister();
}
@Override
protected void afterMethod() throws Exception {
FileUtil.purge(serverPluginsDir.getParentFile(), true);
unprepareForTestAgents();
unprepareScheduler();
try {
cleanupDatabase();
} finally {
unprepareServerPluginService();
unprepareCustomServerService(CoreServerMBean.OBJECT_NAME);
unprepareCustomServerService(PluginDeploymentScannerMBean.OBJECT_NAME);
}
}
private void cleanupDatabase() throws Exception {
try {
getTransactionManager().begin();
Query q;
List<?> doomed;
// remove ResourceTypes which cascade remove BundleTypes
q = em.createQuery("SELECT rt FROM ResourceType rt WHERE rt.deleted = false and rt.name = '"
+ FAKE_RESOURCE_TYPE_NAME + "'");
doomed = q.getResultList();
for (Object removeMe : doomed) {
em.remove(em.getReference(ResourceType.class, ((ResourceType) removeMe).getId()));
}
em.flush();
// remove any orphaned BundleTypes
q = em.createQuery("SELECT bt FROM BundleType bt WHERE bt.name = '" + ANT_BUNDLE_TYPE_NAME + "'");
doomed = q.getResultList();
for (Object removeMe : doomed) {
em.remove(em.getReference(BundleType.class, ((BundleType) removeMe).getId()));
}
getTransactionManager().commit();
} catch (Exception e) {
try {
System.out.println("CANNOT CLEAN UP TEST: Cause: " + e);
getTransactionManager().rollback();
} catch (Exception ignore) {
}
}
}
@Test(groups = RecipeValidationTest.ITESTS)
public void testManageRootDirMandatoryOnBundleVersionCreation() throws Exception {
File root = FileUtil.createTempDirectory(getClass().getName(), null, null);
copyFromClasspath("recipe-no-manageRootDir.xml", "deploy.xml", root);
File bundleZip = createDistributionZip(root);
try {
bundleManager.createBundleVersionViaURL(getFreshOverlord(), bundleZip.toURI().toURL().toString());
fail("A recipe without explicit managerRootDir should not have been created");
} catch (Exception e) {
//expected
checkForExpectedException(e, "org.rhq.bundle.ant.InvalidBuildFileException", "The deployment unit must specifically declare compliance mode of the destination directory.");
} finally {
FileUtil.purge(root, true);
}
}
@Test(groups = RecipeValidationTest.ITESTS)
public void testManageRootDirAbsenceToleratedDuringUpdate() {
// TODO does this even make sense?
}
@Test(groups = RecipeValidationTest.ITESTS)
public void testManageRootDirAbsenceToleratedDuringRevert() {
// TODO implement
}
private Subject getFreshOverlord() {
return LookupUtil.getSubjectManager().getOverlord();
}
private File createDistributionZip(File root) throws IOException {
File ret = File.createTempFile(getClass().getName(),
"zip");
ZipOutputStream distribFile = new ZipOutputStream(new FileOutputStream(ret));
for (File f : getAllFilesRelativeToRoot(root, root)) {
distribFile.putNextEntry(new ZipEntry(f.getPath()));
File resultFile = new File(root, f.getPath());
FileInputStream in = new FileInputStream(resultFile);
try {
StreamUtil.copy(in, distribFile, false);
} finally {
in.close();
}
distribFile.closeEntry();
}
distribFile.close();
return ret;
}
private static Set<File> getAllFilesRelativeToRoot(File parent, File root) {
HashSet<File> ret = new HashSet<File>();
getAllFilesRelativeToRoot(parent, root, ret);
return ret;
}
private static void getAllFilesRelativeToRoot(File parent, File root, Set<File> out) {
for (File f : parent.listFiles()) {
if (f.isDirectory()) {
getAllFilesRelativeToRoot(f, root, out);
} else {
//getRelativePath always prefixes the path with './'. We don't need that.
String path = FileUtil.getRelativePath(f, root).substring(2);
out.add(new File(path));
}
}
}
private void copyFromClasspath(String resourceUrl, String filename, File target)
throws FileNotFoundException, URISyntaxException {
target.getParentFile().mkdirs();
InputStream content = getClass().getResourceAsStream(resourceUrl);
FileOutputStream out = new FileOutputStream(new File(target, filename));
StreamUtil.copy(content, out, true);
}
private void createAntBundleType() throws Exception {
ResourceType rt = createResourceTypeForBundleType();
Subject overlord = LookupUtil.getSubjectManager().getOverlord();
BundleType bt = bundleManager.createBundleType(overlord, ANT_BUNDLE_TYPE_NAME, rt.getId());
assert bt.getId() > 0;
}
private ResourceType createResourceTypeForBundleType() throws Exception {
final String fullName = FAKE_RESOURCE_TYPE_NAME;
ResourceType rt = new ResourceType(fullName, RecipeValidationTest.class.getSimpleName(),
ResourceCategory.PLATFORM, null);
TransactionManager txMgr = getTransactionManager();
txMgr.begin();
em.persist(rt);
txMgr.commit();
return rt;
}
private void checkForExpectedException(Throwable t, String expectedExceptionClassName,
String expectedMessage) {
Throwable test = t;
do {
if (expectedExceptionClassName.equals(test.getClass().getName()) &&
(expectedMessage == null || expectedMessage.equals(test.getMessage()))) {
return;
}
test = test.getCause();
} while (test != null);
fail("Exception " + expectedExceptionClassName +
(expectedMessage == null ? "" : " with message [" + expectedMessage + "]") +
" not detected in the thrown exception " + t);
}
}