diff --git a/controller/src/main/java/org/jboss/as/controller/logging/ControllerLogger.java b/controller/src/main/java/org/jboss/as/controller/logging/ControllerLogger.java index 33d7cd4ecd9..b515c1bceb4 100644 --- a/controller/src/main/java/org/jboss/as/controller/logging/ControllerLogger.java +++ b/controller/src/main/java/org/jboss/as/controller/logging/ControllerLogger.java @@ -3752,4 +3752,7 @@ OperationFailedRuntimeException capabilityAlreadyRegisteredInContext(String capa @Message(id = 504, value = "The operation %s is not defined for resource %s.") UnsupportedOperationException missingOperationForResource(String op, String address); + + @Message(id = 505, value = "Unsuported deployment yaml file %s with attributes %s") + IllegalArgumentException unsupportedDeployment(String deployment, Set attribues); } diff --git a/controller/src/main/java/org/jboss/as/controller/persistence/yaml/YamlConfigurationExtension.java b/controller/src/main/java/org/jboss/as/controller/persistence/yaml/YamlConfigurationExtension.java index bf5f2b8e8f5..a5c1c443e54 100644 --- a/controller/src/main/java/org/jboss/as/controller/persistence/yaml/YamlConfigurationExtension.java +++ b/controller/src/main/java/org/jboss/as/controller/persistence/yaml/YamlConfigurationExtension.java @@ -6,10 +6,16 @@ import static org.jboss.as.controller.client.impl.AdditionalBootCliScriptInvoker.CLI_SCRIPT_PROPERTY; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.BYTES; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.EMPTY; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HASH; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INPUT_STREAM_INDEX; 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.UNDEFINE_ATTRIBUTE_OPERATION; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.URL; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION; import static org.jboss.as.controller.logging.ControllerLogger.MGMT_OP_LOGGER; @@ -27,6 +33,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -74,7 +81,9 @@ public class YamlConfigurationExtension implements ConfigurationExtension { private boolean needReload; private Path[] files; private final List> configs = new ArrayList<>(); - private static final String[] EXCLUDED_ELEMENTS = {" deployment", "extension", "deployment-overlay"}; + private final Map deployments = new LinkedHashMap<>(); + private static final String[] EXCLUDED_ELEMENTS = {"deployment", "extension", "deployment-overlay"}; + public static final Set MANAGED_CONTENT_ATTRIBUTES = Set.of(INPUT_STREAM_INDEX, HASH, BYTES, URL, EMPTY); @SuppressWarnings("unchecked") public YamlConfigurationExtension() { @@ -103,7 +112,10 @@ private void load() { if (yamlConfig.containsKey(CONFIGURATION_ROOT_KEY)) { Map config = (Map) yamlConfig.get(CONFIGURATION_ROOT_KEY); for (String excluded : EXCLUDED_ELEMENTS) { - config.remove(excluded); + Object value = config.remove(excluded); + if(value != null && value instanceof Map && DEPLOYMENT.equals(excluded)) { + deployments.putAll((Map) value); + } } parsedFiles.add(file.toAbsolutePath().toString()); this.configs.add(config); @@ -142,6 +154,9 @@ public void processOperations(ImmutableManagementResourceRegistration rootRegist for (Map config : configs) { processResource(PathAddress.EMPTY_ADDRESS, new HashMap<>(config), rootRegistration, rootRegistration, xmlOperations, postExtensionOps, false); } + for(Map.Entry deployment : deployments.entrySet()) { + processUnmanagedDeployments(rootRegistration, deployment, xmlOperations, postExtensionOps); + } this.configs.clear(); needReload = true; } @@ -471,6 +486,22 @@ public String getCommandLineInstructions() { return MGMT_OP_LOGGER.argYaml(); } + private void processUnmanagedDeployments(ImmutableManagementResourceRegistration rootRegistration, Map.Entry deployment, Map xmlOperations, List postExtensionOps) { + String name = deployment.getKey(); + OperationEntry operationEntry = rootRegistration.getOperationEntry(PathAddress.pathAddress("deployment", name), ADD); + if (deployment.getValue() != null && deployment.getValue() instanceof Map) { + @SuppressWarnings("unchecked") + Map attributes = (Map) deployment.getValue(); + Map content = (Map) (((Iterable)attributes.get("content")).iterator().next()); + Set result = content.keySet().stream().distinct().filter(MANAGED_CONTENT_ATTRIBUTES::contains).collect(Collectors.toSet()); + if (!result.isEmpty()) { + throw MGMT_OP_LOGGER.unsupportedDeployment(name, result); + } + PathAddress address = PathAddress.pathAddress(DEPLOYMENT, name); + processAttributes(address, rootRegistration, operationEntry, attributes, postExtensionOps,xmlOperations); + } + } + private interface Operation { void processOperation(ImmutableManagementResourceRegistration rootRegistration, Map xmlOperations, List postExtensionOps, PathAddress address, String name); } diff --git a/controller/src/test/resources/org/jboss/as/controller/persistence/yaml/deployment.yml b/controller/src/test/resources/org/jboss/as/controller/persistence/yaml/deployment.yml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/controller/src/test/resources/org/jboss/as/controller/persistence/yaml/deployment.yml @@ -0,0 +1 @@ + diff --git a/controller/src/test/resources/org/jboss/as/controller/persistence/yaml/failed_deployment.yml b/controller/src/test/resources/org/jboss/as/controller/persistence/yaml/failed_deployment.yml new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/controller/src/test/resources/org/jboss/as/controller/persistence/yaml/failed_deployment.yml @@ -0,0 +1 @@ + diff --git a/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/persistence/YamlExtensionTestCase.java b/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/persistence/YamlExtensionTestCase.java index ddbd139334a..9d43b47a367 100644 --- a/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/persistence/YamlExtensionTestCase.java +++ b/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/persistence/YamlExtensionTestCase.java @@ -5,14 +5,24 @@ package org.jboss.as.test.manualmode.management.persistence; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ARCHIVE; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CONTENT; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATH; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RELATIVE_TO; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNNING_MODE; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNTIME_NAME; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import jakarta.inject.Inject; +import java.io.BufferedReader; import java.io.IOException; +import java.io.OutputStream; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.StandardOpenOption; import java.util.Iterator; import java.util.regex.Pattern; import org.jboss.as.controller.PathAddress; @@ -23,6 +33,10 @@ import org.jboss.as.test.integration.management.util.CLIWrapper; import org.jboss.as.test.shared.TimeoutUtil; import org.jboss.dmr.ModelNode; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.exporter.ZipExporter; +import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.After; import org.junit.Assert; import org.junit.Assume; @@ -51,8 +65,11 @@ public class YamlExtensionTestCase { @Inject private ServerController container; + private static final Path basedir = new File(WildFlySecurityManager.getPropertyPrivileged("jboss.home", "toto")).toPath().resolve("standalone"); private static Path markerDirectory; private static Path testYaml; + private static Path testDeploymentYaml; + private static Path testManagedDeploymentYaml; private static Path cliScript; private static String expectedXml; private static String expectedBootCLiXml; @@ -61,7 +78,7 @@ public class YamlExtensionTestCase { @BeforeClass public static void setup() throws Exception { Assume.assumeTrue("Layer testing provides a different XML file than the standard one which results in failures", System.getProperty("ts.layers") == null); - Path configurationDir = new File(WildFlySecurityManager.getPropertyPrivileged("jboss.home", "toto")).toPath().resolve("standalone").resolve("configuration"); + Path configurationDir = basedir.resolve("configuration"); Path referenceConfiguration = configurationDir.resolve("reference-standalone.xml"); Files.copy(configurationDir.resolve("standalone.xml"), referenceConfiguration, REPLACE_EXISTING); try (CLIWrapper cli = new CLIWrapper(false)) { @@ -74,8 +91,8 @@ public static void setup() throws Exception { } Path referenceCliConfiguration = configurationDir.resolve("reference-cli-standalone.xml"); Files.copy(configurationDir.resolve("standalone.xml"), referenceCliConfiguration, REPLACE_EXISTING); - Files.copy(new File(YamlExtensionTestCase.class.getResource("bootable-groups.properties").toURI()).toPath(),configurationDir.resolve("bootable-groups.properties"), REPLACE_EXISTING); - Files.copy(new File(YamlExtensionTestCase.class.getResource("bootable-users.properties").toURI()).toPath(),configurationDir.resolve("bootable-users.properties"), REPLACE_EXISTING); + Files.copy(new File(YamlExtensionTestCase.class.getResource("bootable-groups.properties").toURI()).toPath(), configurationDir.resolve("bootable-groups.properties"), REPLACE_EXISTING); + Files.copy(new File(YamlExtensionTestCase.class.getResource("bootable-users.properties").toURI()).toPath(), configurationDir.resolve("bootable-users.properties"), REPLACE_EXISTING); try (CLIWrapper cli = new CLIWrapper(false)) { cli.sendLine("embed-server --admin-only --server-config=reference-cli-standalone.xml"); cli.sendLine("/system-property=foo:add(value=bar)"); @@ -88,6 +105,9 @@ public static void setup() throws Exception { cli.quit(); } testYaml = new File(YamlExtensionTestCase.class.getResource("test.yml").toURI()).toPath().toAbsolutePath(); + testDeploymentYaml = new File(YamlExtensionTestCase.class.getResource("test-deployment.yml").toURI()).toPath().toAbsolutePath(); + testManagedDeploymentYaml = new File(YamlExtensionTestCase.class.getResource("test-managed-deployment.yml").toURI()).toPath().toAbsolutePath(); + createDeployment(configurationDir.getParent().resolve("test.jar")); cliScript = new File(YamlExtensionTestCase.class.getResource("test.cli").toURI()).toPath().toAbsolutePath(); expectedXml = loadFile(referenceConfiguration).replace("\"", "'"); expectedBootCLiXml = loadFile(referenceCliConfiguration).replace("\"", "'"); @@ -138,6 +158,60 @@ public void testSimpleYaml() throws URISyntaxException, Exception { } } + @Test + public void testDeploymentYaml() throws URISyntaxException, Exception { + try { + container.startYamlExtension(new Path[]{testDeploymentYaml}); + try (ModelControllerClient client = container.getClient().getControllerClient()) { + ModelNode deployment = readDeployment(client, "test.jar"); + Assert.assertEquals("test.jar", deployment.get(NAME).asString()); + Assert.assertEquals("test.jar", deployment.get(RUNTIME_NAME).asString()); + ModelNode contentItemNode = deployment.get(CONTENT).get(0); + Assert.assertEquals("test.jar", contentItemNode.get(PATH).asString()); + Assert.assertEquals("jboss.server.base.dir", contentItemNode.get(RELATIVE_TO).asString()); + Assert.assertEquals(true, contentItemNode.get(ARCHIVE).asBoolean()); + deployment = readDeployment(client, "hello.jar"); + Assert.assertEquals("hello.jar", deployment.get(NAME).asString()); + Assert.assertEquals("hello.jar", deployment.get(RUNTIME_NAME).asString()); + contentItemNode = deployment.get(CONTENT).get(0); + Assert.assertEquals("test.jar", contentItemNode.get(PATH).asString()); + Assert.assertEquals("jboss.server.base.dir", contentItemNode.get(RELATIVE_TO).asString()); + Assert.assertEquals(true, contentItemNode.get(ARCHIVE).asBoolean()); + } + } finally { + container.stop(); + } + } + + /** + * Managed deployments are not supported. We should fail + * + * @throws URISyntaxException + * @throws Exception + */ + @Test + public void testFailedDeploymentYaml() throws URISyntaxException, Exception { + try { + container.startYamlExtension(new Path[]{testManagedDeploymentYaml}); + Assert.assertFalse(container.isStarted()); + } catch (RuntimeException ex) { + Assert.assertFalse(container.isStarted()); + try (final BufferedReader reader = Files.newBufferedReader(basedir.resolve("log").resolve("server.log"), StandardCharsets.UTF_8)) { + Assert.assertTrue(reader.lines().anyMatch(line -> line.contains("WFLYCTL0505: Unsuported deployment yaml file hello.jar with attributes [empty]"))); + } + } finally { + container.stop(); + } + } + + private static void createDeployment(Path deployment) throws IOException { + final JavaArchive archive = ShrinkWrap.create(JavaArchive.class); + archive.add(new StringAsset("Dependencies: =org.jboss.modules"), "META-INF/MANIFEST.MF"); + try (OutputStream out = Files.newOutputStream(deployment, StandardOpenOption.CREATE_NEW)) { + archive.as(ZipExporter.class).exportTo(out); + } + } + @Test public void testSimpleYamlWithReload() throws URISyntaxException, Exception { try { @@ -206,7 +280,10 @@ private void compareXML(String expected, String result) { Assert.fail("Missing line " + expectedLines[i] + " in " + System.lineSeparator() + result); } } + } + private ModelNode readDeployment(ModelControllerClient client, String deploymentName) throws IOException { + return Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("deployment", deploymentName).toModelNode()))); } private String readXmlConfig() throws IOException { @@ -218,4 +295,5 @@ private String readXmlConfig() throws IOException { private static String removeWhiteSpaces(String line) { return Pattern.compile("(^\\s*$\\r?\\n)+", Pattern.MULTILINE).matcher(line.stripTrailing()).replaceAll(""); } + } diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/test-deployment.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/test-deployment.yml new file mode 100644 index 00000000000..c53e42dea63 --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/test-deployment.yml @@ -0,0 +1,14 @@ +wildfly-configuration: + deployment: + test.jar: + content: + - + path: test.jar + relative-to: jboss.server.base.dir + archive: true + hello.jar: + content: + - + path: test.jar + relative-to: jboss.server.base.dir + archive: true \ No newline at end of file diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/test-managed-deployment.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/test-managed-deployment.yml new file mode 100644 index 00000000000..61c84f5ee03 --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/test-managed-deployment.yml @@ -0,0 +1,12 @@ +wildfly-configuration: + deployment: + test.jar: + content: + - + path: test.jar + relative-to: jboss.server.base.dir + archive: true + hello.jar: + content: + - + empty: true \ No newline at end of file