From c6933a6cc2c27182e4dddde2708d1ee2714936af Mon Sep 17 00:00:00 2001 From: Michal Jurc Date: Mon, 29 Aug 2016 23:13:56 +0200 Subject: [PATCH] [WFCORE-429] Incremental redeployment (single file update) over management API Adding DeploymentOperationsTestCase for testing hard requirements on deployment operations. --- .../core/DeploymentOperationsTestCase.java | 463 ++++++++++++++++++ 1 file changed, 463 insertions(+) create mode 100644 testsuite/standalone/src/test/java/org/wildfly/core/test/standalone/mgmt/api/core/DeploymentOperationsTestCase.java diff --git a/testsuite/standalone/src/test/java/org/wildfly/core/test/standalone/mgmt/api/core/DeploymentOperationsTestCase.java b/testsuite/standalone/src/test/java/org/wildfly/core/test/standalone/mgmt/api/core/DeploymentOperationsTestCase.java new file mode 100644 index 00000000000..2ba2b486700 --- /dev/null +++ b/testsuite/standalone/src/test/java/org/wildfly/core/test/standalone/mgmt/api/core/DeploymentOperationsTestCase.java @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2016 Red Hat, inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.wildfly.core.test.standalone.mgmt.api.core; + +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD_CONTENT; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ARCHIVE; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.BYTES; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.COMPOSITE; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CONTENT; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOY; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.EXPLODE; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.MANAGED; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OVERWRITE; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATH; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATHS; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ATTRIBUTE_OPERATION; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_CONTENT; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_RESOURCE_OPERATION; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REMOVE_CONTENT; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.STEPS; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.TARGET_PATH; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Properties; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import javax.inject.Inject; + +import org.jboss.as.controller.client.ModelControllerClient; +import org.jboss.as.controller.client.OperationBuilder; +import org.jboss.as.controller.client.helpers.Operations; +import org.jboss.as.controller.client.helpers.standalone.DeploymentAction; +import org.jboss.as.controller.client.helpers.standalone.DeploymentPlan; +import org.jboss.as.controller.client.helpers.standalone.ServerDeploymentActionResult; +import org.jboss.as.controller.client.helpers.standalone.ServerDeploymentManager; +import org.jboss.as.controller.client.helpers.standalone.ServerDeploymentPlanResult; +import org.jboss.as.test.deployment.trivial.ServiceActivatorDeploymentUtil; +import org.jboss.as.test.shared.TimeoutUtil; +import org.jboss.dmr.ModelNode; +import org.jboss.shrinkwrap.api.exporter.ExplodedExporter; +import org.jboss.shrinkwrap.api.exporter.ZipExporter; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.wildfly.core.testrunner.ManagementClient; +import org.wildfly.core.testrunner.WildflyTestRunner; + +/** + * Tests to check hard requirements and extreme cases for deployment operations. + * @author Michal Jurc (c) 2016 Red Hat, Inc. + */ +@RunWith(WildflyTestRunner.class) +public class DeploymentOperationsTestCase { + + @Inject + private ManagementClient managementClient; + + private static final int TIMEOUT = TimeoutUtil.adjust(20000); + private static final String TEST_DEPLOYMENT_NAME = "test-deployment.jar"; + + @After + public void cleanDeployments() { + ModelNode op = new ModelNode(); + op.get(OP).set(READ_RESOURCE_OPERATION); + op.get(OP_ADDR).set(DEPLOYMENT, TEST_DEPLOYMENT_NAME); + ModelNode result = awaitOperationExecutionAndReturnOutcome(op); + if (Operations.isSuccessfulOutcome(result)) { + undeployAndRemoveDeployment(TEST_DEPLOYMENT_NAME); + } + } + + @AfterClass + public static void cleanFiles() { + String jbossBaseDir = System.getProperty("jboss.home"); + Assert.assertNotNull(jbossBaseDir); + Path dataDir = new File(jbossBaseDir).toPath().resolve("standalone").resolve("data"); + Assert.assertTrue(Files.exists(dataDir)); + cleanFile(dataDir.resolve("managed-exploded").toFile()); + cleanFile(new File("target", "archives")); + } + + @Test + public void testManagedAttributeValue() throws Exception { + deployManagedDeployment(TEST_DEPLOYMENT_NAME, true); + Assert.assertTrue(getManagedAttribute(TEST_DEPLOYMENT_NAME)); + undeployAndRemoveDeployment(TEST_DEPLOYMENT_NAME); + deployManagedDeployment(TEST_DEPLOYMENT_NAME, false); + Assert.assertTrue(getManagedAttribute(TEST_DEPLOYMENT_NAME)); + undeployAndRemoveDeployment(TEST_DEPLOYMENT_NAME); + deployUnmanagedDeployment(TEST_DEPLOYMENT_NAME, true); + Assert.assertFalse(getManagedAttribute(TEST_DEPLOYMENT_NAME)); + undeployAndRemoveDeployment(TEST_DEPLOYMENT_NAME); + deployUnmanagedDeployment(TEST_DEPLOYMENT_NAME, false); + Assert.assertFalse(getManagedAttribute(TEST_DEPLOYMENT_NAME)); + undeployAndRemoveDeployment(TEST_DEPLOYMENT_NAME); + } + + @Test + public void testManagedAttributeReadOnly() throws Exception { + deployManagedDeployment(TEST_DEPLOYMENT_NAME, true); + Assert.assertFalse(writeManagedAttributeAndGetOutcome(TEST_DEPLOYMENT_NAME, false)); + undeployAndRemoveDeployment(TEST_DEPLOYMENT_NAME); + deployManagedDeployment(TEST_DEPLOYMENT_NAME, false); + Assert.assertFalse(writeManagedAttributeAndGetOutcome(TEST_DEPLOYMENT_NAME, false)); + undeployAndRemoveDeployment(TEST_DEPLOYMENT_NAME); + deployUnmanagedDeployment(TEST_DEPLOYMENT_NAME, true); + Assert.assertFalse(writeManagedAttributeAndGetOutcome(TEST_DEPLOYMENT_NAME, true)); + undeployAndRemoveDeployment(TEST_DEPLOYMENT_NAME); + deployUnmanagedDeployment(TEST_DEPLOYMENT_NAME, false); + Assert.assertFalse(writeManagedAttributeAndGetOutcome(TEST_DEPLOYMENT_NAME, true)); + undeployAndRemoveDeployment(TEST_DEPLOYMENT_NAME); + } + + @Test + public void testManagedExplodedOperationsFailWithUnmanagedArchiveDeployment() throws Exception { + deployUnmanagedDeployment(TEST_DEPLOYMENT_NAME, true); + Assert.assertFalse(explodeDeploymentAndGetOutcome(TEST_DEPLOYMENT_NAME)); + Assert.assertEquals(ServerDeploymentActionResult.Result.ROLLED_BACK, addContentToDeploymentAndGetOutcome(TEST_DEPLOYMENT_NAME).getResult()); + Assert.assertFalse(removeContentFromDeploymentAndGetOutcome(TEST_DEPLOYMENT_NAME)); + Assert.assertFalse(readContentFromDeploymentAndGetOutcome(TEST_DEPLOYMENT_NAME)); + undeployAndRemoveDeployment(TEST_DEPLOYMENT_NAME); + } + + @Test + public void testManagedExplodedOperationsFailWithUnmanagedExplodedDeployment() throws Exception { + deployUnmanagedDeployment(TEST_DEPLOYMENT_NAME, false); + Assert.assertFalse(explodeDeploymentAndGetOutcome(TEST_DEPLOYMENT_NAME)); + Assert.assertEquals(ServerDeploymentActionResult.Result.ROLLED_BACK, addContentToDeploymentAndGetOutcome(TEST_DEPLOYMENT_NAME).getResult()); + Assert.assertFalse(removeContentFromDeploymentAndGetOutcome(TEST_DEPLOYMENT_NAME)); + Assert.assertFalse(readContentFromDeploymentAndGetOutcome(TEST_DEPLOYMENT_NAME)); + undeployAndRemoveDeployment(TEST_DEPLOYMENT_NAME); + } + + @Test + public void testManagedExplodedOperationsFailWithManagedArchiveDeployment() throws Exception { + deployManagedDeployment(TEST_DEPLOYMENT_NAME, true); + Assert.assertFalse(explodeDeploymentAndGetOutcome(TEST_DEPLOYMENT_NAME)); + Assert.assertEquals(ServerDeploymentActionResult.Result.ROLLED_BACK, addContentToDeploymentAndGetOutcome(TEST_DEPLOYMENT_NAME).getResult()); + Assert.assertFalse(removeContentFromDeploymentAndGetOutcome(TEST_DEPLOYMENT_NAME)); + Assert.assertFalse(readContentFromDeploymentAndGetOutcome(TEST_DEPLOYMENT_NAME)); + undeployAndRemoveDeployment(TEST_DEPLOYMENT_NAME); + } + + @Test + public void testExplodeFailsWithDeployedManagedArchiveDeployment() throws Exception { + deployManagedDeployment(TEST_DEPLOYMENT_NAME, true); + Assert.assertFalse(explodeDeploymentAndGetOutcome(TEST_DEPLOYMENT_NAME)); + undeployAndRemoveDeployment(TEST_DEPLOYMENT_NAME); + } + + @Test + public void testAddContentFailsWithoutOverwriteManagedExplodedDeployment() throws Exception { + deployManagedDeployment(TEST_DEPLOYMENT_NAME, false); + + final Properties addedContentProperties = new Properties(); + final ModelControllerClient client = managementClient.getControllerClient(); + addedContentProperties.put(TEST_DEPLOYMENT_NAME + "Service", "isReplaced"); + String addedContentString; + try (StringWriter writer = new StringWriter()) { + addedContentProperties.store(writer, "Added with add-content op"); + addedContentString = writer.toString(); + } + + ModelNode addedContentNode = new ModelNode(); + addedContentNode.get(BYTES).set(addedContentString.getBytes(StandardCharsets.UTF_8)); + addedContentNode.get(TARGET_PATH).set("SimpleTest.properties"); + ModelNode addContentOp = new ModelNode(); + addContentOp.get(OP).set(ADD_CONTENT); + addContentOp.get(OP_ADDR).set(DEPLOYMENT, TEST_DEPLOYMENT_NAME); + addContentOp.get(CONTENT).setEmptyList(); + addContentOp.get(CONTENT).add(addedContentNode); + addContentOp.get(OVERWRITE).add(false); + + Future future = client.executeAsync(OperationBuilder.create(addContentOp, false).build(), null); + ModelNode response; + try { + response = future.get(TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } catch (ExecutionException e) { + throw new RuntimeException(e.getCause()); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + + Assert.assertNotNull(response); + Assert.assertFalse("Operation add-content over existing content in deployment with overwrite=false succeeded, should fail", + Operations.isSuccessfulOutcome(response)); + + undeployAndRemoveDeployment(TEST_DEPLOYMENT_NAME); + } + + + private boolean getManagedAttribute(String deploymentName) { + ModelNode op = new ModelNode(); + op.get(OP).set(READ_ATTRIBUTE_OPERATION); + op.get(OP_ADDR).add(DEPLOYMENT, deploymentName); + op.get(NAME).set(MANAGED); + Future future = managementClient.getControllerClient().executeAsync(OperationBuilder.create(op, false).build(), null); + try { + ModelNode response = future.get(TIMEOUT, TimeUnit.MILLISECONDS); + return Operations.readResult(response).asBoolean(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } catch (ExecutionException e) { + throw new RuntimeException(e.getCause()); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + } + + private boolean writeManagedAttributeAndGetOutcome(String deploymentName, boolean newValue) { + ModelNode op = new ModelNode(); + op.get(OP).set(WRITE_ATTRIBUTE_OPERATION); + op.get(OP_ADDR).add(DEPLOYMENT, deploymentName); + op.get(NAME).set(MANAGED); + op.get(VALUE).set(newValue); + + return Operations.isSuccessfulOutcome(awaitOperationExecutionAndReturnOutcome(op)); + } + + private boolean explodeDeploymentAndGetOutcome(String deploymentName) { + ModelNode op = new ModelNode(); + op.get(OP).set(EXPLODE); + op.get(OP_ADDR).add(DEPLOYMENT, deploymentName); + + return Operations.isSuccessfulOutcome(awaitOperationExecutionAndReturnOutcome(op)); + } + + private ServerDeploymentActionResult addContentToDeploymentAndGetOutcome(String deploymentName) throws Exception { + final Properties addedProperties = new Properties(); + addedProperties.put(deploymentName + "Service", "isAdded"); + String content; + try (StringWriter writer = new StringWriter()){ + addedProperties.store(writer, "New Content"); + content = writer.toString(); + } + final ModelControllerClient client = managementClient.getControllerClient(); + final ServerDeploymentManager manager = ServerDeploymentManager.Factory.create(client); + + try (InputStream is = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))) { + DeploymentPlan plan = manager.newDeploymentPlan() + .addContentToDeployment(deploymentName, Collections.singletonMap("SimpleTest.properties", is)) + .redeploy(deploymentName) + .build(); + Future future = manager.execute(plan); + try { + ServerDeploymentPlanResult planResult = future.get(TIMEOUT, TimeUnit.MILLISECONDS); + UUID addContentAction = null; + for (DeploymentAction action : plan.getDeploymentActions()) { + if (action.getType().equals(DeploymentAction.Type.ADD_CONTENT)){ + addContentAction = action.getId(); + } + } + Assert.assertNotNull(addContentAction); + return planResult.getDeploymentActionResult(addContentAction); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } catch (ExecutionException e) { + throw new RuntimeException(e.getCause()); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + } + } + + private boolean removeContentFromDeploymentAndGetOutcome(String deploymentName) throws Exception { + ModelNode op = new ModelNode(); + op.get(OP).set(REMOVE_CONTENT); + op.get(OP_ADDR).add(DEPLOYMENT, deploymentName); + op.get(PATHS).setEmptyList(); + op.get(PATHS).add("simpleTest.properties"); + + return Operations.isSuccessfulOutcome(awaitOperationExecutionAndReturnOutcome(op)); + } + + private boolean readContentFromDeploymentAndGetOutcome(String deploymentName) throws Exception { + ModelNode op = new ModelNode(); + op.get(OP).set(READ_CONTENT); + op.get(OP_ADDR).add(DEPLOYMENT, deploymentName); + op.get(PATH).set("simpleTest.properties"); + + return Operations.isSuccessfulOutcome(awaitOperationExecutionAndReturnOutcome(op)); + } + + private void deployManagedDeployment(String deploymentName, boolean archived) throws Exception { + final Properties properties = new Properties(); + properties.put(deploymentName + "Service", "isNew"); + final JavaArchive archive = ServiceActivatorDeploymentUtil.createServiceActivatorDeploymentArchive(deploymentName, properties); + final ModelControllerClient client = managementClient.getControllerClient(); + final ServerDeploymentManager manager = ServerDeploymentManager.Factory.create(client); + + try (InputStream is = archive.as(ZipExporter.class).exportAsInputStream()) { + if (archived) { + Future future = manager.execute(manager.newDeploymentPlan() + .add(deploymentName, is) + .deploy(deploymentName) + .build()); + awaitDeploymentExecution(future); + } else { + Future future = manager.execute(manager.newDeploymentPlan() + .add(deploymentName, is) + .explodeDeployment(deploymentName) + .deploy(deploymentName) + .build()); + awaitDeploymentExecution(future); + } + } + + ServiceActivatorDeploymentUtil.validateProperties(client, properties); + } + + private void deployUnmanagedDeployment(String deploymentName, boolean archived) throws Exception { + final Properties properties = new Properties(); + properties.put(deploymentName + "Service", "isNew"); + final JavaArchive archive = ServiceActivatorDeploymentUtil.createServiceActivatorDeploymentArchive(deploymentName, properties); + final ModelControllerClient client = managementClient.getControllerClient(); + final File archiveDir = new File("target/archives"); + archiveDir.mkdirs(); + File deploymentFile; + if (archived) { + deploymentFile = new File(archiveDir, deploymentName); + archive.as(ZipExporter.class).exportTo(deploymentFile, true); + } else { + archive.as(ExplodedExporter.class).exportExploded(archiveDir); + deploymentFile = new File(archiveDir, deploymentName); + } + + ModelNode compositeOp = new ModelNode(); + + ModelNode content = new ModelNode(); + content.get(PATH).set(deploymentFile.getAbsolutePath()); + content.get(ARCHIVE).set(archived); + ModelNode addOp = new ModelNode(); + addOp.get(OP).set(ADD); + addOp.get(OP_ADDR).add(DEPLOYMENT, deploymentName); + addOp.get(CONTENT).set(content); + ModelNode deployOp = new ModelNode(); + deployOp.get(OP).set(DEPLOY); + deployOp.get(OP_ADDR).add(DEPLOYMENT, deploymentName); + + compositeOp.get(OP).set(COMPOSITE); + compositeOp.get(OP_ADDR).setEmptyList(); + compositeOp.get(STEPS).add(addOp); + compositeOp.get(STEPS).add(deployOp); + awaitOperationExecution(compositeOp); + + ServiceActivatorDeploymentUtil.validateProperties(client, properties); + } + + private void undeployAndRemoveDeployment(String deploymentName) { + final ModelControllerClient client = managementClient.getControllerClient(); + final ServerDeploymentManager manager = ServerDeploymentManager.Factory.create(client); + + Future future = manager.execute(manager.newDeploymentPlan() + .undeploy(deploymentName) + .remove(deploymentName) + .build()); + + awaitDeploymentExecution(future); + + File deploymentFile = new File("target/archives/" + deploymentName); + if (deploymentFile.exists()) { + cleanFile(deploymentFile); + } + } + + private void awaitOperationExecution(ModelNode op) { + Future future = managementClient.getControllerClient().executeAsync(OperationBuilder.create(op, false).build(), null); + try { + ModelNode response = future.get(TIMEOUT, TimeUnit.MILLISECONDS); + Assert.assertTrue(Operations.isSuccessfulOutcome(response)); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } catch (ExecutionException e) { + throw new RuntimeException(e.getCause()); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + } + + private ModelNode awaitOperationExecutionAndReturnOutcome(ModelNode op) { + Future future = managementClient.getControllerClient().executeAsync(OperationBuilder.create(op, false).build(), null); + try { + return future.get(TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } catch (ExecutionException e) { + throw new RuntimeException(e.getCause()); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + } + + private void awaitDeploymentExecution(Future future) { + try { + future.get(TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } catch (ExecutionException e) { + throw new RuntimeException(e.getCause()); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + } + + private static void cleanFile(File toClean) { + if (toClean.exists()) { + if (toClean.isDirectory()) { + for (File child : toClean.listFiles()) { + cleanFile(child); + } + } + toClean.delete(); + } + } + +}