/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. You may obtain a * copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package org.apache.geode.management.internal.cli.commands; import static org.apache.geode.distributed.ConfigurationProperties.ENABLE_CLUSTER_CONFIGURATION; import static org.apache.geode.distributed.ConfigurationProperties.GROUPS; import static org.apache.geode.distributed.ConfigurationProperties.HTTP_SERVICE_PORT; import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER; import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_BIND_ADDRESS; import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_PORT; import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_START; import static org.apache.geode.distributed.ConfigurationProperties.LOG_LEVEL; import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT; import static org.apache.geode.distributed.ConfigurationProperties.NAME; import static org.apache.geode.test.dunit.Assert.assertEquals; import static org.apache.geode.test.dunit.Assert.assertFalse; import static org.apache.geode.test.dunit.Assert.assertTrue; import static org.apache.geode.test.dunit.Assert.fail; import static org.assertj.core.api.Assertions.assertThat; import org.apache.geode.distributed.Locator; import org.apache.geode.distributed.internal.DistributionManager; import org.apache.geode.distributed.internal.InternalLocator; import org.apache.geode.distributed.internal.ClusterConfigurationService; import org.apache.geode.internal.AvailablePort; import org.apache.geode.internal.AvailablePortHelper; import org.apache.geode.internal.ClassBuilder; import org.apache.geode.internal.JarDeployer; import org.apache.geode.management.cli.Result; import org.apache.geode.management.internal.cli.i18n.CliStrings; import org.apache.geode.management.internal.cli.remote.CommandExecutionContext; import org.apache.geode.management.internal.cli.remote.CommandProcessor; import org.apache.geode.management.internal.cli.result.CommandResult; import org.apache.geode.test.dunit.Assert; import org.apache.geode.test.dunit.Host; import org.apache.geode.test.dunit.SerializableRunnable; import org.apache.geode.test.dunit.VM; import org.apache.geode.test.dunit.Wait; import org.apache.geode.test.dunit.WaitCriterion; import org.apache.geode.test.dunit.rules.ServerStarterRule; import org.apache.geode.test.junit.categories.DistributedTest; import org.junit.Test; import org.junit.experimental.categories.Category; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Properties; import java.util.regex.Pattern; /** * Unit tests for the DeployCommands class * * @since GemFire 7.0 */ @SuppressWarnings("serial") @Category(DistributedTest.class) public class DeployCommandsDUnitTest extends CliCommandTestBase { private final Pattern pattern = Pattern.compile("^" + JarDeployer.JAR_PREFIX + "DeployCommandsDUnit.*#\\d++$"); private File newDeployableJarFile; private transient ClassBuilder classBuilder; private transient CommandProcessor commandProcessor; @Override public final void postSetUpCliCommandTestBase() throws Exception { this.newDeployableJarFile = new File(this.temporaryFolder.getRoot().getCanonicalPath() + File.separator + "DeployCommandsDUnit1.jar"); this.classBuilder = new ClassBuilder(); this.commandProcessor = new CommandProcessor(); assertFalse(this.commandProcessor.isStopped()); Host.getHost(0).getVM(0).invoke(new SerializableRunnable() { public void run() { deleteSavedJarFiles(); } }); deleteSavedJarFiles(); } @SuppressWarnings("serial") @Override protected final void preTearDownCliCommandTestBase() throws Exception { Host.getHost(0).getVM(1).invoke(new SerializableRunnable() { public void run() { DistributionManager.isDedicatedAdminVM = false; } }); Host.getHost(0).getVM(0).invoke(new SerializableRunnable() { public void run() { deleteSavedJarFiles(); } }); deleteSavedJarFiles(); } @Test public void testDeploy() throws Exception { final Properties props = new Properties(); final Host host = Host.getHost(0); final VM vm = host.getVM(0); final String vmName = "VM" + vm.getPid(); // Create the cache in this VM props.setProperty(NAME, "Controller"); props.setProperty(GROUPS, "Group1"); getSystem(props); getCache(); // Create the cache in the other VM vm.invoke(new SerializableRunnable() { public void run() { props.setProperty(NAME, vmName); props.setProperty(GROUPS, "Group2"); getSystem(props); getCache(); } }); DeployCommands deployCommands = new DeployCommands(); // Single JAR all members CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit1.jar".getBytes(), this.classBuilder.createJarFromName("DeployCommandsDUnitA")}); Result result = deployCommands.deploy(null, "DeployCommandsDUnit1.jar", null); assertEquals(true, result.hasNextLine()); String resultString = result.nextLine(); assertEquals(false, resultString.contains("ERROR")); assertEquals(1, countMatchesInString(resultString, "Controller")); assertEquals(1, countMatchesInString(resultString, vmName)); assertEquals(4, countMatchesInString(resultString, "DeployCommandsDUnit1.jar")); // Single JAR with group CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit2.jar".getBytes(), this.classBuilder.createJarFromName("DeployCommandsDUnitB")}); result = deployCommands.deploy(new String[] {"Group2"}, "DeployCommandsDUnit2.jar", null); assertEquals(true, result.hasNextLine()); resultString = result.nextLine(); assertEquals(false, resultString.contains("ERROR")); assertEquals(false, resultString.contains("Controller")); assertEquals(1, countMatchesInString(resultString, vmName)); assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit2.jar")); // Multiple JARs to all members CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit3.jar".getBytes(), this.classBuilder.createJarFromName("DeployCommandsDUnitC"), "DeployCommandsDUnit4.jar".getBytes(), this.classBuilder.createJarFromName("DeployCommandsDUnitD")}); result = deployCommands.deploy(null, null, "AnyDirectory"); assertEquals(true, result.hasNextLine()); resultString = result.nextLine(); assertEquals(false, resultString.contains("ERROR")); assertEquals(2, countMatchesInString(resultString, "Controller")); assertEquals(2, countMatchesInString(resultString, vmName)); assertEquals(4, countMatchesInString(resultString, "DeployCommandsDUnit3.jar")); assertEquals(4, countMatchesInString(resultString, "DeployCommandsDUnit4.jar")); // Multiple JARs to a group CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit5.jar".getBytes(), this.classBuilder.createJarFromName("DeployCommandsDUnitE"), "DeployCommandsDUnit6.jar".getBytes(), this.classBuilder.createJarFromName("DeployCommandsDUnitF")}); result = deployCommands.deploy(new String[] {"Group1"}, null, "AnyDirectory"); assertEquals(true, result.hasNextLine()); resultString = result.nextLine(); assertEquals(false, resultString.contains("ERROR")); assertEquals(2, countMatchesInString(resultString, "Controller")); assertEquals(false, resultString.contains(vmName)); assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit5.jar")); assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit6.jar")); } @Test public void testUndeploy() throws Exception { final Properties props = new Properties(); final Host host = Host.getHost(0); final VM vm = host.getVM(0); final String vmName = "VM" + vm.getPid(); // Create the cache in this VM props.setProperty(NAME, "Controller"); props.setProperty(GROUPS, "Group1"); getSystem(props); getCache(); // Create the cache in the other VM vm.invoke(new SerializableRunnable() { public void run() { props.setProperty(NAME, vmName); props.setProperty(GROUPS, "Group2"); getSystem(props); getCache(); } }); DeployCommands deployCommands = new DeployCommands(); // Deploy a couple of JAR files which can be undeployed CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit1.jar".getBytes(), this.classBuilder.createJarFromName("DeployCommandsDUnitA")}); deployCommands.deploy(new String[] {"Group1"}, "DeployCommandsDUnit1.jar", null); CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit2.jar".getBytes(), this.classBuilder.createJarFromName("DeployCommandsDUnitB")}); deployCommands.deploy(new String[] {"Group2"}, "DeployCommandsDUnit2.jar", null); CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit3.jar".getBytes(), this.classBuilder.createJarFromName("DeployCommandsDUnitC")}); deployCommands.deploy(null, "DeployCommandsDUnit3.jar", null); CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit4.jar".getBytes(), this.classBuilder.createJarFromName("DeployCommandsDUnitD")}); deployCommands.deploy(null, "DeployCommandsDUnit4.jar", null); CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit5.jar".getBytes(), this.classBuilder.createJarFromName("DeployCommandsDUnitE")}); deployCommands.deploy(null, "DeployCommandsDUnit5.jar", null); // Undeploy for 1 group Result result = deployCommands.undeploy(new String[] {"Group1"}, "DeployCommandsDUnit1.jar"); assertEquals(true, result.hasNextLine()); String resultString = result.nextLine(); assertEquals(false, resultString.contains("ERROR")); assertEquals(1, countMatchesInString(resultString, "Controller")); assertEquals(false, resultString.contains(vmName)); assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit1.jar")); // Multiple Undeploy for all members result = deployCommands.undeploy(null, "DeployCommandsDUnit2.jar, DeployCommandsDUnit3.jar"); assertEquals(true, result.hasNextLine()); resultString = result.nextLine(); assertEquals(false, resultString.contains("ERROR")); assertEquals(2, countMatchesInString(resultString, "Controller")); assertEquals(2, countMatchesInString(resultString, vmName)); assertEquals(3, countMatchesInString(resultString, "DeployCommandsDUnit2.jar")); assertEquals(4, countMatchesInString(resultString, "DeployCommandsDUnit3.jar")); // Undeploy all (no JAR specified) result = deployCommands.undeploy(null, null); assertEquals(true, result.hasNextLine()); resultString = result.nextLine(); assertEquals(false, resultString.contains("ERROR")); assertEquals(2, countMatchesInString(resultString, "Controller")); assertEquals(2, countMatchesInString(resultString, vmName)); assertEquals(4, countMatchesInString(resultString, "DeployCommandsDUnit4.jar")); assertEquals(4, countMatchesInString(resultString, "DeployCommandsDUnit5.jar")); } @Test public void testListDeployed() throws Exception { final Properties props = new Properties(); final Host host = Host.getHost(0); final VM vm = host.getVM(0); final String vmName = "VM" + vm.getPid(); // Create the cache in this VM props.setProperty(NAME, "Controller"); props.setProperty(GROUPS, "Group1"); getSystem(props); getCache(); // Create the cache in the other VM vm.invoke(new SerializableRunnable() { public void run() { props.setProperty(NAME, vmName); props.setProperty(GROUPS, "Group2"); getSystem(props); getCache(); } }); DeployCommands deployCommands = new DeployCommands(); // Deploy a couple of JAR files which can be listed CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit1.jar".getBytes(), this.classBuilder.createJarFromName("DeployCommandsDUnitA")}); deployCommands.deploy(new String[] {"Group1"}, "DeployCommandsDUnit1.jar", null); CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit2.jar".getBytes(), this.classBuilder.createJarFromName("DeployCommandsDUnitB")}); deployCommands.deploy(new String[] {"Group2"}, "DeployCommandsDUnit2.jar", null); // List for all members Result result = deployCommands.listDeployed(null); assertEquals(true, result.hasNextLine()); String resultString = result.nextLine(); assertEquals(false, resultString.contains("ERROR")); assertEquals(1, countMatchesInString(resultString, "Controller")); assertEquals(1, countMatchesInString(resultString, vmName)); assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit1.jar")); assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit2.jar")); // List for members in Group1 result = deployCommands.listDeployed("Group1"); assertEquals(true, result.hasNextLine()); resultString = result.nextLine(); assertEquals(false, resultString.contains("ERROR")); assertEquals(1, countMatchesInString(resultString, "Controller")); assertEquals(false, resultString.contains(vmName)); assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit1.jar")); assertEquals(false, resultString.contains("DeployCommandsDUnit2.jar")); // List for members in Group2 result = deployCommands.listDeployed("Group2"); assertEquals(true, result.hasNextLine()); resultString = result.nextLine(); assertEquals(false, resultString.contains("ERROR")); assertEquals(false, resultString.contains("Controller")); assertEquals(1, countMatchesInString(resultString, vmName)); assertEquals(false, resultString.contains("DeployCommandsDUnit1.jar")); assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit2.jar")); } /** * Does an end-to-end test using the complete CLI framework while ensuring that the shared * configuration is updated. */ @Test public void testEndToEnd() throws Exception { final String groupName = getName(); final int[] ports = AvailablePortHelper.getRandomAvailableTCPPorts(2); jmxPort = ports[0]; httpPort = ports[1]; try { jmxHost = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException ignore) { jmxHost = "localhost"; } // Start the Locator and wait for shared configuration to be available final int locatorPort = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); final String locatorLogPath = this.temporaryFolder.getRoot().getCanonicalPath() + File.separator + "locator-" + locatorPort + ".log"; final Properties locatorProps = new Properties(); locatorProps.setProperty(NAME, "Locator"); locatorProps.setProperty(MCAST_PORT, "0"); locatorProps.setProperty(LOG_LEVEL, "fine"); locatorProps.setProperty(ENABLE_CLUSTER_CONFIGURATION, "true"); locatorProps.setProperty(JMX_MANAGER, "true"); locatorProps.setProperty(JMX_MANAGER_START, "true"); locatorProps.setProperty(JMX_MANAGER_BIND_ADDRESS, String.valueOf(jmxHost)); locatorProps.setProperty(JMX_MANAGER_PORT, String.valueOf(jmxPort)); locatorProps.setProperty(HTTP_SERVICE_PORT, String.valueOf(httpPort)); Host.getHost(0).getVM(0).invoke(new SerializableRunnable() { @Override public void run() { final File locatorLogFile = new File(locatorLogPath); try { final InternalLocator locator = (InternalLocator) Locator.startLocatorAndDS(locatorPort, locatorLogFile, null, locatorProps); WaitCriterion wc = new WaitCriterion() { @Override public boolean done() { return locator.isSharedConfigurationRunning(); } @Override public String description() { return "Waiting for shared configuration to be started"; } }; Wait.waitForCriterion(wc, 5000, 500, true); } catch (IOException e) { fail("Unable to create a locator with a shared configuration", e); } } }); connect(jmxHost, jmxPort, httpPort, getDefaultShell()); Host.getHost(0).getVM(1).invoke(() -> { Properties properties = new Properties(); properties.setProperty("name", "Manager"); properties.setProperty("groups", groupName); ServerStarterRule serverStarterRule = new ServerStarterRule(properties); serverStarterRule.startServer(locatorPort); }); // Create a JAR file this.classBuilder.writeJarFromName("DeployCommandsDUnitA", this.newDeployableJarFile); // Deploy the JAR CommandResult cmdResult = executeCommand("deploy --jar=" + this.newDeployableJarFile.getCanonicalPath()); assertEquals(Result.Status.OK, cmdResult.getStatus()); String stringResult = commandResultToString(cmdResult); assertEquals(3, countLinesInString(stringResult, false)); assertTrue(stringContainsLine(stringResult, "Member.*JAR.*JAR Location")); assertTrue(stringContainsLine(stringResult, "Manager.*DeployCommandsDUnit1.jar.*" + JarDeployer.JAR_PREFIX + "DeployCommandsDUnit1.jar#1")); // Undeploy the JAR cmdResult = executeCommand("undeploy --jar=DeployCommandsDUnit1.jar"); assertEquals(Result.Status.OK, cmdResult.getStatus()); stringResult = commandResultToString(cmdResult); assertEquals(3, countLinesInString(stringResult, false)); assertThat(stringContainsLine(stringResult, "Member.*JAR.*Un-Deployed From JAR Location")) .describedAs(stringResult).isTrue(); assertThat(stringContainsLine(stringResult, "Manager.*DeployCommandsDUnit1.jar.*" + JarDeployer.JAR_PREFIX + "DeployCommandsDUnit1.jar#1")).describedAs(stringResult) .isTrue();; // Deploy the JAR to a group cmdResult = executeCommand( "deploy --jar=" + this.newDeployableJarFile.getCanonicalPath() + " --group=" + groupName); assertThat(cmdResult.getStatus()).describedAs(cmdResult.toString()).isEqualTo(Result.Status.OK); stringResult = commandResultToString(cmdResult); assertEquals(3, countLinesInString(stringResult, false)); assertThat(stringContainsLine(stringResult, "Member.*JAR.*JAR Location")) .describedAs(stringResult).isTrue(); assertThat(stringContainsLine(stringResult, "Manager.*DeployCommandsDUnit1.jar.*" + JarDeployer.JAR_PREFIX + "DeployCommandsDUnit1.jar#1")).describedAs(stringResult) .isTrue(); // Make sure the deployed jar in the shared config Host.getHost(0).getVM(0).invoke(new SerializableRunnable() { @Override public void run() { ClusterConfigurationService sharedConfig = ((InternalLocator) Locator.getLocator()).getSharedConfiguration(); try { assertTrue(sharedConfig.getConfiguration(groupName).getJarNames() .contains("DeployCommandsDUnit1.jar")); } catch (Exception e) { Assert.fail("Error occurred in cluster configuration service", e); } } }); // List deployed for group cmdResult = executeCommand("list deployed --group=" + groupName); assertEquals(Result.Status.OK, cmdResult.getStatus()); stringResult = commandResultToString(cmdResult); assertEquals(3, countLinesInString(stringResult, false)); assertTrue(stringContainsLine(stringResult, "Member.*JAR.*JAR Location")); assertTrue(stringContainsLine(stringResult, "Manager.*DeployCommandsDUnit1.jar.*" + JarDeployer.JAR_PREFIX + "DeployCommandsDUnit1.jar#1")); // Undeploy for group cmdResult = executeCommand("undeploy --group=" + groupName); assertEquals(Result.Status.OK, cmdResult.getStatus()); stringResult = commandResultToString(cmdResult); assertEquals(3, countLinesInString(stringResult, false)); assertTrue(stringContainsLine(stringResult, "Member.*JAR.*Un-Deployed From JAR Location")); assertTrue(stringContainsLine(stringResult, "Manager.*DeployCommandsDUnit1.jar.*" + JarDeployer.JAR_PREFIX + "DeployCommandsDUnit1.jar#1")); // Make sure the deployed jar was removed from the shared config Host.getHost(0).getVM(0).invoke(new SerializableRunnable() { @Override public void run() { ClusterConfigurationService sharedConfig = ((InternalLocator) Locator.getLocator()).getSharedConfiguration(); try { assertFalse(sharedConfig.getConfiguration(groupName).getJarNames() .contains("DeployCommandsDUnit1.jar")); } catch (Exception e) { Assert.fail("Error occurred in cluster configuration service", e); } } }); // List deployed with nothing deployed cmdResult = executeCommand("list deployed"); assertEquals(Result.Status.OK, cmdResult.getStatus()); assertTrue( commandResultToString(cmdResult).contains(CliStrings.LIST_DEPLOYED__NO_JARS_FOUND_MESSAGE)); } private void deleteSavedJarFiles() { this.newDeployableJarFile.delete(); File dirFile = new File("."); // Find all deployed JAR files File[] oldJarFiles = dirFile.listFiles(new FilenameFilter() { @Override public boolean accept(final File file, final String name) { return DeployCommandsDUnitTest.this.pattern.matcher(name).matches(); } }); // Now delete them if (oldJarFiles != null) { for (File oldJarFile : oldJarFiles) { oldJarFile.delete(); } } } }