From ee81c4f7380f2fde06dd4eefa0452abd0cb3897d Mon Sep 17 00:00:00 2001
From: Emmanuel Hugonnet <ehugonne@redhat.com>
Date: Mon, 10 Jun 2024 17:02:31 +0200
Subject: [PATCH] [WFCORE-6503]:Add support for unmanaged deployments with YAML
 extension.

* checking that the YAML deployment is unmanaged.
* adding the unmanaged deployment to the list of operations
* adding some light testing on this

Jira: https://issues.redhat.com/browse/WFCORE-6503
Proposal: https://github.com/wildfly/wildfly-proposals/pull/554

Signed-off-by: Emmanuel Hugonnet <ehugonne@redhat.com>
---
 .../yaml/YamlConfigurationExtension.java      | 40 +++++++++++++++--
 .../jboss/as/server/ServerEnvironment.java    |  5 ---
 .../yaml/YamlExtensionTestCase.java           | 43 +++++++++++++++++++
 .../persistence/yaml/test-deployment.yml      | 14 ++++++
 .../yaml/test-managed-deployment.yml          | 12 ++++++
 .../persistence/yaml/test-operations.yml      |  1 -
 6 files changed, 105 insertions(+), 10 deletions(-)
 create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-deployment.yml
 create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-managed-deployment.yml

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 4794322b94d..b6821494b10 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,11 +6,17 @@
 
 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.REMOVE;
 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;
@@ -28,6 +34,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;
@@ -77,7 +84,9 @@ public class YamlConfigurationExtension implements ConfigurationExtension {
     private boolean needReload;
     private Path[] files;
     private final List<Map<String, Object>> configs = new ArrayList<>();
+    private final Map<String, Object> deployments = new LinkedHashMap<>();
     private static final String[] EXCLUDED_ELEMENTS = {"deployment", "extension", "deployment-overlay", "path"};
+    public static final Set<String> MANAGED_CONTENT_ATTRIBUTES = Set.of(INPUT_STREAM_INDEX, HASH, BYTES, URL, EMPTY);
 
     @SuppressWarnings("unchecked")
     public YamlConfigurationExtension() {
@@ -114,11 +123,15 @@ private void load() {
                         boolean isPresent = config.containsKey(excluded);
                         if (isPresent) {
                             Object value = config.remove(excluded);
-                            String message = MGMT_OP_LOGGER.ignoreYamlElement(excluded);
-                            if (value != null) {
-                                message = message + MGMT_OP_LOGGER.ignoreYamlSubElement(yaml.dump(value).trim());
+                            if (value != null && value instanceof Map && DEPLOYMENT.equals(excluded)) {
+                                deployments.putAll((Map<String, Object>) value);
+                            } else {
+                                String message = MGMT_OP_LOGGER.ignoreYamlElement(excluded);
+                                if (value != null) {
+                                    message = message + MGMT_OP_LOGGER.ignoreYamlSubElement(yaml.dump(value).trim());
+                                }
+                                MGMT_OP_LOGGER.warn(message);
                             }
-                            MGMT_OP_LOGGER.warn(message);
                         }
                     }
                     parsedFiles.add(file.toAbsolutePath().toString());
@@ -160,6 +173,9 @@ public void processOperations(ImmutableManagementResourceRegistration rootRegist
         for (Map<String, Object> config : configs) {
             processResource(PathAddress.EMPTY_ADDRESS, new HashMap<>(config), rootRegistration, rootRegistration, xmlOperations, postExtensionOps, false);
         }
+        for (Map.Entry<String, Object> deployment : deployments.entrySet()) {
+            processUnmanagedDeployments(rootRegistration, deployment, xmlOperations, postExtensionOps);
+        }
         this.configs.clear();
         needReload = true;
     }
@@ -524,6 +540,22 @@ public String getCommandLineInstructions() {
         return MGMT_OP_LOGGER.argYaml();
     }
 
+    @SuppressWarnings("unchecked")
+    private void processUnmanagedDeployments(ImmutableManagementResourceRegistration rootRegistration, Map.Entry<String, Object> deployment, Map<PathAddress, ParsedBootOp> xmlOperations, List<ParsedBootOp> postExtensionOps) {
+        String name = deployment.getKey();
+        OperationEntry operationEntry = rootRegistration.getOperationEntry(PathAddress.pathAddress("deployment", name), ADD);
+        if (deployment.getValue() != null && deployment.getValue() instanceof Map) {
+            Map<String, Object> attributes = (Map<String, Object>) deployment.getValue();
+            Map<String, Object> content = (Map<String, Object>) (((Iterable<? extends Object>) attributes.get("content")).iterator().next());
+            Set<String> 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 {
 
         String getOperationName();
diff --git a/server/src/main/java/org/jboss/as/server/ServerEnvironment.java b/server/src/main/java/org/jboss/as/server/ServerEnvironment.java
index 35646ff3eea..718b80bc24c 100644
--- a/server/src/main/java/org/jboss/as/server/ServerEnvironment.java
+++ b/server/src/main/java/org/jboss/as/server/ServerEnvironment.java
@@ -347,11 +347,6 @@ public ServerEnvironment(final String hostControllerName, final Properties props
         javaExtDirs = getFilesFromProperty(JAVA_EXT_DIRS, props);
 
         if (launchType.equals(LaunchType.SELF_CONTAINED)) {
-            Path[] supplementalConfigurationFiles = findSupplementalConfigurationFiles(null, supplementalConfiguration);
-            ConfigurationExtension configurationExtension = ConfigurationExtensionFactory.createConfigurationExtension(supplementalConfigurationFiles);
-            if (configurationExtension != null) {
-                configInteractionPolicy = configurationExtension.shouldProcessOperations(runningModeControl) ? ConfigurationFile.InteractionPolicy.READ_ONLY : configInteractionPolicy;
-            }
             homeDir = new File(WildFlySecurityManager.getPropertyPrivileged("user.dir", "."));
             serverBaseDir = new File(WildFlySecurityManager.getPropertyPrivileged("user.dir", "."));
             serverLogDir = new File(WildFlySecurityManager.getPropertyPrivileged("user.dir", "."));
diff --git a/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/persistence/yaml/YamlExtensionTestCase.java b/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/persistence/yaml/YamlExtensionTestCase.java
index 7094d2f656f..0010be9ce5d 100644
--- a/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/persistence/yaml/YamlExtensionTestCase.java
+++ b/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/persistence/yaml/YamlExtensionTestCase.java
@@ -6,9 +6,15 @@
 
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 import static org.hamcrest.MatcherAssert.assertThat;
+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.OUTCOME;
+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.RESULT;
 import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNNING_MODE;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNTIME_NAME;
 import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS;
 import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.UUID;
 
@@ -145,6 +151,8 @@ public static void setup() throws Exception {
         testRemoveSocketYaml = getResourceFilePath("test-remove-socket.yml");
         testAddingExtensionPathDeploymentOverlayIgnored = getResourceFilePath("test-adding-extension-path-deployment-overlay-ignored.yml");
         testAddingEmptyExtensionFailYaml = getResourceFilePath("test-adding-empty-extension.yml");
+        testDeploymentYaml = getResourceFilePath("test-deployment.yml");
+        testManagedDeploymentYaml = getResourceFilePath("test-managed-deployment.yml");
         testReplacingByEmptyResourceYaml = getResourceFilePath("test-replacing-by-empty-resource.yml");
         testWrongIndentationYaml = getResourceFilePath("test-indentation-wrong.yml");
         testNonExistentAttributeYaml = getResourceFilePath("test-setting-non-existent-attribute.yml");
@@ -154,6 +162,7 @@ public static void setup() throws Exception {
         testRemoveNonExistentResource = getResourceFilePath("test-remove-non-existent-resource.yml");
         testListAddOperationToStringFails = getResourceFilePath("test-list-add-operation-to-string-fails.yml");
         testListAddOperationToNonExistentResourceFails = getResourceFilePath("test-list-add-operation-to-non-existent-resource.yml");
+        createDeployment(configurationDir.getParent().resolve("test.jar"));
         cliScript = getResourceFilePath("test.cli");
         defaultXml = loadFile(configurationDir.resolve("standalone.xml")).replace("\"", "'");
         expectedXml = loadFile(referenceConfiguration).replace("\"", "'");
@@ -248,6 +257,40 @@ public void testEmptyExtensionInYamlLogsWarnings() throws Exception {
         assertThat("Information that adding path is ignored is missing in log.", byteArrayOutputStream.toString(), CoreMatchers.containsString("WFLYCTL0508: The yaml element 'extension' and its sub-elements are ignored."));
     }
 
+    @Test
+    public void testDeploymentYaml() throws Exception {
+        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.assertTrue(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.assertTrue(contentItemNode.get(ARCHIVE).asBoolean());
+        }
+    }
+
+    /**
+     * Managed deployments are not supported. We should fail
+     */
+    @Test
+    public void testServerStartFailedForManagedDeployment() {
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        try {
+            container.startYamlExtension(new PrintStream(byteArrayOutputStream), new Path[]{testManagedDeploymentYaml});
+            Assert.assertFalse(container.isStarted());
+        } catch (RuntimeException ex) {
+            Assert.assertFalse(container.isStarted());
+        }
+    }
 
     private static void createDeployment(Path deployment) throws IOException {
         final JavaArchive archive = ShrinkWrap.create(JavaArchive.class);
diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-deployment.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/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/yaml/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/yaml/test-managed-deployment.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/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/yaml/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
diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-operations.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-operations.yml
index 4e1d7f7ddbd..520782bff56 100644
--- a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-operations.yml
+++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-operations.yml
@@ -4,7 +4,6 @@ wildfly-configuration:
     standard-sockets:
       socket-binding:
         management-https: !remove
-
   subsystem:
     elytron:
       disallowed-providers: !undefine