From fdd41718d8e062b68f1a9c868ba470d5c3d1de69 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Fri, 5 Apr 2024 11:32:07 +0200 Subject: [PATCH] [WFCORE-6771]: Fixing several small issues with the YAML extension. * adding WARNING traces * adding warning if YAML is adding an existing resource without any attribute * failing if unexisting attribute is used * better error messages * ignoring removal of unexisting resource * warning about ignoring unexisting resource delete operation * Fixing how we apply YAML configuration on reload * Adding configuration for YAML file size. * Adding tests for yaml Jira: https://issues.redhat.com/browse/WFCORE-6771 Signed-off-by: Emmanuel Hugonnet --- .../as/controller/ModelControllerImpl.java | 2 +- .../as/controller/RunningModeControl.java | 15 + .../controller/logging/ControllerLogger.java | 27 +- .../BackupXmlConfigurationPersister.java | 4 + .../persistence/ConfigurationExtension.java | 6 +- .../persistence/ConfigurationPersister.java | 11 + .../NullConfigurationPersister.java | 5 + .../XmlConfigurationPersister.java | 7 + .../yaml/YamlConfigurationExtension.java | 126 +++- .../yaml/YamlConfigurationExtensionTest.java | 4 +- .../persistence/yaml/deployment.yml | 1 + .../persistence/yaml/failed_deployment.yml | 1 + .../launcher/BootableJarCommandBuilder.java | 37 + .../core/launcher/CommandBuilderTest.java | 15 +- .../test/StringConfigurationPersister.java | 7 + .../jboss/as/server/ServerEnvironment.java | 4 +- .../ServerRootResourceDefinition.java | 5 +- .../ServerDomainProcessReloadHandler.java | 2 +- .../ServerProcessReloadHandler.java | 27 +- .../persistence/YamlExtensionTestCase.java | 221 ------ .../yaml/YamlExtensionTestCase.java | 645 ++++++++++++++++++ .../{ => yaml}/bootable-groups.properties | 0 .../{ => yaml}/bootable-users.properties | 0 .../yaml/test-adding-empty-extension.yml | 3 + ...ension-path-deployment-overlay-ignored.yml | 11 + .../yaml/test-indentation-wrong.yml | 6 + ...add-operation-to-non-existent-resource.yml | 7 + ...est-list-add-operation-to-string-fails.yml | 8 + .../yaml/test-non-existent-resource.yml | 6 + .../persistence/yaml/test-operations.yml | 17 + .../yaml/test-port-offset-override.yml | 5 + ...emove-attribute-removes-above-resource.yml | 7 + .../test-remove-non-existent-resource.yml | 6 + .../persistence/yaml/test-remove-socket.yml | 6 + .../yaml/test-replacing-by-empty-resource.yml | 6 + .../test-setting-non-existent-attribute.yml | 6 + .../persistence/yaml/test-socket-override.yml | 11 + .../test-undefine-non-existent-attribute.yml | 7 + .../persistence/{ => yaml}/test.cli | 0 .../persistence/{ => yaml}/test.yml | 1 + .../org/wildfly/core/testrunner/Server.java | 12 + .../core/testrunner/ServerController.java | 6 +- 42 files changed, 1032 insertions(+), 271 deletions(-) create mode 100644 controller/src/test/resources/org/jboss/as/controller/persistence/yaml/deployment.yml create mode 100644 controller/src/test/resources/org/jboss/as/controller/persistence/yaml/failed_deployment.yml delete mode 100644 testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/persistence/YamlExtensionTestCase.java create mode 100644 testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/persistence/yaml/YamlExtensionTestCase.java rename testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/{ => yaml}/bootable-groups.properties (100%) rename testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/{ => yaml}/bootable-users.properties (100%) create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-adding-empty-extension.yml create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-adding-extension-path-deployment-overlay-ignored.yml create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-indentation-wrong.yml create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-list-add-operation-to-non-existent-resource.yml create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-list-add-operation-to-string-fails.yml create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-non-existent-resource.yml create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-operations.yml create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-port-offset-override.yml create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-remove-attribute-removes-above-resource.yml create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-remove-non-existent-resource.yml create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-remove-socket.yml create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-replacing-by-empty-resource.yml create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-setting-non-existent-attribute.yml create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-socket-override.yml create mode 100644 testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-undefine-non-existent-attribute.yml rename testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/{ => yaml}/test.cli (100%) rename testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/{ => yaml}/test.yml (99%) diff --git a/controller/src/main/java/org/jboss/as/controller/ModelControllerImpl.java b/controller/src/main/java/org/jboss/as/controller/ModelControllerImpl.java index 76ea986bd27..c9f2e5f4557 100644 --- a/controller/src/main/java/org/jboss/as/controller/ModelControllerImpl.java +++ b/controller/src/main/java/org/jboss/as/controller/ModelControllerImpl.java @@ -518,7 +518,7 @@ boolean boot(final List bootList, final OperationMessageHandler handl headers, handler, null, managementModel.get(), control, processState, auditLogger, bootingFlag.get(), true, hostServerGroupTracker, null, notificationSupport, true, extraValidationStepHandler, partialModel, securityIdentitySupplier); - if (configExtension != null && configExtension.shouldProcessOperations(runningModeControl.getRunningMode())) { + if (configExtension != null && configExtension.shouldProcessOperations(runningModeControl)) { configExtension.processOperations(managementModel.get().getRootResourceRegistration(), bootOperations.postExtensionOps); } for (ParsedBootOp parsedOp : bootOperations.postExtensionOps) { diff --git a/controller/src/main/java/org/jboss/as/controller/RunningModeControl.java b/controller/src/main/java/org/jboss/as/controller/RunningModeControl.java index 4274c9ff3d0..5c899c1b269 100644 --- a/controller/src/main/java/org/jboss/as/controller/RunningModeControl.java +++ b/controller/src/main/java/org/jboss/as/controller/RunningModeControl.java @@ -17,6 +17,7 @@ public class RunningModeControl { private volatile boolean useCurrentConfig; private volatile String newBootFileName; private volatile Boolean suspend; + private volatile boolean applyConfigurationExtension; public RunningModeControl(final RunningMode initialMode) { this.runningMode = initialMode; @@ -58,6 +59,20 @@ public void setSuspend(Boolean suspend) { this.suspend = suspend; } + /** + * Indicates if the configuration extension should be applied after reloading. + * This should occur on a reload if no changes were applied (thus stored) or after the changes made + * by a boot cli script. + * @return true if we should apply the configuration extension - false otherwise. + */ + public boolean isApplyConfigurationExtension() { + return applyConfigurationExtension; + } + + public void setApplyConfigurationExtension(boolean applyConfigurationExtension) { + this.applyConfigurationExtension = applyConfigurationExtension; + } + /** * Get the new boot file name. For a standalone server this will be the location of the server configuration * (i.e. the standalone.xml variety). For a host controller this will be the location of the host configuration 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 7a48a72942e..47622d56808 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 @@ -3682,7 +3682,7 @@ OperationFailedRuntimeException capabilityAlreadyRegisteredInContext(String capa IllegalArgumentException noResourceForUndefiningAttribute(String attribute, String address); @LogMessage(level = WARN) - @Message(id = 490, value = "You have defined a resource for address %s without any attributes, doing nothing") + @Message(id = 490, value = "A YAML resource has been defined for the address %s without any attribute. No actions will be taken.") void noAttributeSetForAddress(String address); @LogMessage(level = WARN) @@ -3744,7 +3744,7 @@ OperationFailedRuntimeException capabilityAlreadyRegisteredInContext(String capa @Message(id = 501, value = "An invalid UUID string '%s' was found at '%s'. A new value will be generated.") void uuidNotValid(String corruptedUuid, String path); - @Message(id = 502, value = "No child resource called %s could be found at address %s'.") + @Message(id = 502, value = "No child resource called '%s' could be found at address '%s'.") IllegalArgumentException noChildResource(String name, String address); @Message(id = 503, value = "Failed to publish configuration, because the remote name %s is not valid.") @@ -3759,4 +3759,27 @@ OperationFailedRuntimeException capabilityAlreadyRegisteredInContext(String capa @LogMessage(level = WARN) @Message(id = 506, value = "Extension %s from module %s is not enabled by the current stability level") void unstableExtension(String extensionName, String moduleName); + + @Message(id = 507, value = "Unsuported deployment yaml file %s with attributes %s") + IllegalArgumentException unsupportedDeployment(String deployment, Set attributes); + + @Message(id = 508, value = "The yaml element '%s' and its sub-elements are ignored.") + String ignoreYamlElement(String element); + + @Message(id = NONE, value = " Thus ignoring element '%s'.") + String ignoreYamlSubElement(String element); + + @Message(id = 509, value = "No attribute called '%s' is defined at address '%s'.") + IllegalArgumentException noAttributeDefined(String name, String address); + + @Message(id = 510, value = "No operation %s can be executed for attribute called '%s' is defined at address '%s'.") + IllegalArgumentException illegalOperationForAttribute(String operationName, String attribute, String address); + + @LogMessage(level = WARN) + @Message(id = 511, value = "No value is defined for attribute '%s' at address '%s'.") + void noAttributeValueDefined(String name, String address); + + @LogMessage(level = WARN) + @Message(id = 512, value = "No resource exists at address '%s'. Ignoring the remove opreation.") + void removingUnexistingResource(String address); } diff --git a/controller/src/main/java/org/jboss/as/controller/persistence/BackupXmlConfigurationPersister.java b/controller/src/main/java/org/jboss/as/controller/persistence/BackupXmlConfigurationPersister.java index 1302ca8e62c..6c428cb20c8 100644 --- a/controller/src/main/java/org/jboss/as/controller/persistence/BackupXmlConfigurationPersister.java +++ b/controller/src/main/java/org/jboss/as/controller/persistence/BackupXmlConfigurationPersister.java @@ -68,6 +68,7 @@ private static boolean isSuppressLoad(ConfigurationFile configurationFile, boole return initialEmpty && !reload; } + @Override public void registerAdditionalRootElement(final QName anotherRoot, final XMLElementReader> parser){ super.registerAdditionalRootElement(anotherRoot, parser); } @@ -93,13 +94,16 @@ public boolean isPersisting() { public PersistenceResource store(final ModelNode model, Set affectedAddresses) throws ConfigurationPersistenceException { if(!successfulBoot.get()) { return new PersistenceResource() { + @Override public void commit() { } + @Override public void rollback() { } }; } + this.stored = true; return new ConfigurationFilePersistenceResource(model, configurationFile, this); } diff --git a/controller/src/main/java/org/jboss/as/controller/persistence/ConfigurationExtension.java b/controller/src/main/java/org/jboss/as/controller/persistence/ConfigurationExtension.java index 10e3a242af0..1c4481f620c 100644 --- a/controller/src/main/java/org/jboss/as/controller/persistence/ConfigurationExtension.java +++ b/controller/src/main/java/org/jboss/as/controller/persistence/ConfigurationExtension.java @@ -8,7 +8,7 @@ import java.nio.file.Path; import java.util.List; import org.jboss.as.controller.ParsedBootOp; -import org.jboss.as.controller.RunningMode; +import org.jboss.as.controller.RunningModeControl; import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration; /** @@ -44,10 +44,10 @@ public interface ConfigurationExtension { /** * Checks if the configuration extension should process the supplemental configurations. - * @param mode: the running mode of the server. + * @param runningModeControl: the running mode control of the server. * @return true if the configuration extension should process operations - false otherwise. */ - boolean shouldProcessOperations(RunningMode mode); + boolean shouldProcessOperations(RunningModeControl runningModeControl); /** * Process the already defined boot operations to update them with the supplemnetal configurations. diff --git a/controller/src/main/java/org/jboss/as/controller/persistence/ConfigurationPersister.java b/controller/src/main/java/org/jboss/as/controller/persistence/ConfigurationPersister.java index 32ad21a7724..e05a06dea5f 100644 --- a/controller/src/main/java/org/jboss/as/controller/persistence/ConfigurationPersister.java +++ b/controller/src/main/java/org/jboss/as/controller/persistence/ConfigurationPersister.java @@ -50,6 +50,17 @@ default boolean isPersisting() { return true; } + /** + * Gets whether a call persist to persistent storage has been successfully completed. + *

+ * The default implementation always returns {@code false} + * + * @return {@code true} if a call to {@link #store(ModelNode, Set)} will return an object that actually writes + */ + default boolean hasStored() { + return false; + } + /** * Persist the given configuration model if {@link #isPersisting()} would return {@code true}, otherwise * return a no-op {@link PersistenceResource}. diff --git a/controller/src/main/java/org/jboss/as/controller/persistence/NullConfigurationPersister.java b/controller/src/main/java/org/jboss/as/controller/persistence/NullConfigurationPersister.java index 51d38e7b7c0..c7f11592a92 100644 --- a/controller/src/main/java/org/jboss/as/controller/persistence/NullConfigurationPersister.java +++ b/controller/src/main/java/org/jboss/as/controller/persistence/NullConfigurationPersister.java @@ -40,6 +40,11 @@ public List load() { return Collections.emptyList(); } + @Override + public boolean hasStored() { + return false; + } + private static class NullPersistenceResource implements ConfigurationPersister.PersistenceResource { private static final NullPersistenceResource INSTANCE = new NullPersistenceResource(); diff --git a/controller/src/main/java/org/jboss/as/controller/persistence/XmlConfigurationPersister.java b/controller/src/main/java/org/jboss/as/controller/persistence/XmlConfigurationPersister.java index 4e0f223f318..7ee06195b0b 100644 --- a/controller/src/main/java/org/jboss/as/controller/persistence/XmlConfigurationPersister.java +++ b/controller/src/main/java/org/jboss/as/controller/persistence/XmlConfigurationPersister.java @@ -44,6 +44,7 @@ public class XmlConfigurationPersister extends AbstractConfigurationPersister { private final XMLElementReader> rootParser; private final Map>> additionalParsers; private final boolean suppressLoad; + protected volatile boolean stored = false; /** * Construct a new instance. @@ -84,6 +85,7 @@ public void registerAdditionalRootElement(final QName anotherRoot, final XMLElem /** {@inheritDoc} */ @Override public PersistenceResource store(final ModelNode model, Set affectedAddresses) throws ConfigurationPersistenceException { + stored = true; return new FilePersistenceResource(model, fileName, this); } @@ -157,4 +159,9 @@ protected void successfulBoot(File file) throws ConfigurationPersistenceExceptio } + @Override + public boolean hasStored() { + return isPersisting() && stored; + } + } 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..4794322b94d 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 @@ -9,6 +9,7 @@ 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.VALUE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION; @@ -40,6 +41,7 @@ import org.jboss.as.controller.ParsedBootOp; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.RunningMode; +import org.jboss.as.controller.RunningModeControl; import org.jboss.as.controller.persistence.ConfigurationExtension; import org.jboss.as.controller.registry.AttributeAccess; import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration; @@ -67,6 +69,7 @@ public class YamlConfigurationExtension implements ConfigurationExtension { private static final String CONFIGURATION_ROOT_KEY = "wildfly-configuration"; + private static final String YAML_CODEPOINT_LIMIT = "org.wildfly.configuration.extension.yaml.codepoint.limit"; private static final String YAML_CONFIG = "--yaml"; private static final String SHORT_YAML_CONFIG = "-y"; @@ -74,7 +77,7 @@ 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 static final String[] EXCLUDED_ELEMENTS = {"deployment", "extension", "deployment-overlay", "path"}; @SuppressWarnings("unchecked") public YamlConfigurationExtension() { @@ -94,8 +97,13 @@ private void load() { for (Path file : files) { if (file != null && Files.exists(file) && Files.isRegularFile(file)) { Map yamlConfig = Collections.emptyMap(); + LoaderOptions loadingConfig = new LoaderOptions(); + //Default to 3MB + loadingConfig.setCodePointLimit( + Integer.parseInt( + WildFlySecurityManager.getPropertyPrivileged(YAML_CODEPOINT_LIMIT, "3145728"))); + Yaml yaml = new Yaml(new OperationConstructor(loadingConfig)); try (InputStream inputStream = Files.newInputStream(file)) { - Yaml yaml = new Yaml(new OperationConstructor(new LoaderOptions())); yamlConfig = yaml.load(inputStream); } catch (IOException ioex) { throw MGMT_OP_LOGGER.failedToParseYamlConfigurationFile(file.toAbsolutePath().toString(), ioex); @@ -103,7 +111,15 @@ 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); + 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()); + } + MGMT_OP_LOGGER.warn(message); + } } parsedFiles.add(file.toAbsolutePath().toString()); this.configs.add(config); @@ -117,8 +133,10 @@ private void load() { } @Override - public boolean shouldProcessOperations(RunningMode mode) { - return (!this.configs.isEmpty() || (needReload && this.files.length > 0)) && (RunningMode.ADMIN_ONLY != mode || null == WildFlySecurityManager.getPropertyPrivileged(CLI_SCRIPT_PROPERTY, null)); + public boolean shouldProcessOperations(RunningModeControl runningModeControl) { + return (!this.configs.isEmpty() || (needReload && this.files.length > 0)) + && (RunningMode.ADMIN_ONLY != runningModeControl.getRunningMode() || null == WildFlySecurityManager.getPropertyPrivileged(CLI_SCRIPT_PROPERTY, null)) + && (!runningModeControl.isReloaded() || runningModeControl.isApplyConfigurationExtension()); } @SuppressWarnings("unchecked") @@ -174,7 +192,7 @@ private void processResource(PathAddress parentAddress, Map yaml } else { if (value == null && !isExistingResource(xmlOperations, address)) { //empty resource OperationEntry operationEntry = rootRegistration.getOperationEntry(address, ADD); - if(operationEntry != null) { + if (operationEntry != null) { processAttributes(address, rootRegistration, operationEntry, Collections.emptyMap(), postExtensionOps, xmlOperations); } else { throw MGMT_OP_LOGGER.missingOperationForResource("ADD", address.toCLIStyleString()); @@ -184,13 +202,20 @@ private void processResource(PathAddress parentAddress, Map yaml if (isExistingResource(xmlOperations, address)) { yamlOperation.processOperation(rootRegistration, xmlOperations, postExtensionOps, address, name); } else if (yamlOperation instanceof RemoveOperation) { - //ignore + MGMT_OP_LOGGER.removingUnexistingResource(address.toCLIStyleString()); } else { - throw MGMT_OP_LOGGER.noResourceForUndefiningAttribute(name, address.toCLIStyleString()); + if (yamlOperation instanceof UndefineOperation) { + throw MGMT_OP_LOGGER.noResourceForUndefiningAttribute(name, address.toCLIStyleString()); + } + throw MGMT_OP_LOGGER.illegalOperationForAttribute(yamlOperation.getOperationName(), name, address.toCLIStyleString()); } } else { if (!isExistingResource(xmlOperations, address)) { - MGMT_OP_LOGGER.noAttributeSetForAddress(address.toCLIStyleString()); + if (resourceRegistration.getAttributeNames(PathAddress.EMPTY_ADDRESS).contains(name)) { + MGMT_OP_LOGGER.noAttributeValueDefined(name, address.toCLIStyleString()); + } else { + MGMT_OP_LOGGER.noAttributeSetForAddress(address.toCLIStyleString()); + } } } } @@ -203,13 +228,13 @@ private void processResource(PathAddress parentAddress, Map yaml Object value = yaml.get(name); if (value instanceof Map) { Map map = (Map) value; - if (resourceRegistration.getAttributeNames(PathAddress.EMPTY_ADDRESS).contains(name) && - resourceRegistration.getAttributeAccess(PathAddress.EMPTY_ADDRESS, name).getAttributeDefinition().getType() == OBJECT) { + if (resourceRegistration.getAttributeNames(PathAddress.EMPTY_ADDRESS).contains(name) + && resourceRegistration.getAttributeAccess(PathAddress.EMPTY_ADDRESS, name).getAttributeDefinition().getType() == OBJECT) { processAttribute(address, rootRegistration, name, value, postExtensionOps, xmlOperations); - } else if( !address.equals(parentAddress)) { + } else if (!address.equals(parentAddress)) { processResource(address, map, rootRegistration, rootRegistration.getSubModel(address), xmlOperations, postExtensionOps, false); } else { - throw MGMT_OP_LOGGER.noChildResource(name , address.toCLIStyleString()); + throw MGMT_OP_LOGGER.noChildResource(name, address.toCLIStyleString()); } } else if (value instanceof Operation) { Operation yamlOperation = Operation.class.cast(value); @@ -217,8 +242,16 @@ private void processResource(PathAddress parentAddress, Map yaml } else { if (value != null && resourceRegistration.getAttributeNames(PathAddress.EMPTY_ADDRESS).contains(name)) { //we are processing an attribute: - MGMT_OP_LOGGER.debugf("We are processing the attribute %s for address %s", name, address.getParent().toCLIStyleString()); + MGMT_OP_LOGGER.debugf("We are processing the attribute %s for address %s", name, parentAddress.toCLIStyleString()); processAttribute(parentAddress, rootRegistration, name, value, postExtensionOps, xmlOperations); + } else if (value == null) { + if (resourceRegistration.getAttributeNames(PathAddress.EMPTY_ADDRESS).contains(name)) { + MGMT_OP_LOGGER.noAttributeValueDefined(name, address.toCLIStyleString()); + } else { + MGMT_OP_LOGGER.noAttributeSetForAddress(address.toCLIStyleString()); + } + } else { + throw MGMT_OP_LOGGER.noAttributeDefined(name, address.toCLIStyleString()); } } } else { @@ -233,8 +266,8 @@ private void processResource(PathAddress parentAddress, Map yaml } else { if (!postExtensionOps.isEmpty()) { ParsedBootOp op = postExtensionOps.get(postExtensionOps.size() - 1); - if (! address.equals(op.getAddress())) { // else already processed - Map map = value instanceof Map ? new HashMap<>((Map)value) : new HashMap<>(yaml); + if (!address.equals(op.getAddress())) { // else already processed + Map map = value instanceof Map ? new HashMap<>((Map) value) : new HashMap<>(yaml); //need to process attributes for adding processAttributes(address, rootRegistration, operationEntry, map, postExtensionOps, xmlOperations); processResource(address, map, rootRegistration, resourceRegistration, xmlOperations, postExtensionOps, false); @@ -254,7 +287,15 @@ private void processResource(PathAddress parentAddress, Map yaml processResource(address, map, rootRegistration, childResourceRegistration, xmlOperations, postExtensionOps, false); } else { if (value != null) { - MGMT_OP_LOGGER.unexpectedValueForResource(value, address.toCLIStyleString(), name); + if (value instanceof Operation) { + if (value instanceof RemoveOperation) { + MGMT_OP_LOGGER.removingUnexistingResource(address.toCLIStyleString()); + } else { + MGMT_OP_LOGGER.illegalOperationForAttribute(((Operation) value).getOperationName(), name, address.toCLIStyleString()); + } + } else { + MGMT_OP_LOGGER.unexpectedValueForResource(value, address.toCLIStyleString(), name); + } } } } else if (name.equals(address.getLastElement().getValue())) { @@ -266,7 +307,15 @@ private void processResource(PathAddress parentAddress, Map yaml processResource(address, map, rootRegistration, childResourceRegistration, xmlOperations, postExtensionOps, false); } else { if (value != null) { - MGMT_OP_LOGGER.unexpectedValueForResource(value, address.toCLIStyleString(), name); + if (value instanceof Operation) { + if (value instanceof RemoveOperation) { + MGMT_OP_LOGGER.removingUnexistingResource(address.toCLIStyleString()); + } else { + MGMT_OP_LOGGER.illegalOperationForAttribute(((Operation) value).getOperationName(), name, address.toCLIStyleString()); + } + } else { + MGMT_OP_LOGGER.unexpectedValueForResource(value, address.toCLIStyleString(), name); + } } else {// ADD operation without parameters processAttributes(address, rootRegistration, operationEntry, null, postExtensionOps, xmlOperations); } @@ -376,7 +425,11 @@ private void processAttributes(PathAddress address, ImmutableManagementResourceR processListAttribute((ListAttributeDefinition) att, list, value); break; default: - op.get(att.getName()).set(value.toString()); + if (value != null) { + op.get(att.getName()).set(value.toString()); + } else { + op.get(att.getName()); + } break; } } @@ -385,7 +438,7 @@ private void processAttributes(PathAddress address, ImmutableManagementResourceR ParsedBootOp operation = new ParsedBootOp(op, operationEntry.getOperationHandler()); MGMT_OP_LOGGER.debugf("Adding resource with operation %s", op); postExtensionOps.add(operation); - if(ADD.equals(operationEntry.getOperationDefinition().getName())) { + if (ADD.equals(operationEntry.getOperationDefinition().getName())) { xmlOperations.put(address, operation); } } @@ -405,8 +458,8 @@ private ModelNode processObjectAttribute(ObjectTypeAttributeDefinition att, Map< Object value = map.get(child.getName()); switch (child.getType()) { case OBJECT: - if(child instanceof MapAttributeDefinition) { - processMapAttribute((MapAttributeDefinition)child, objectNode, (Map)value); + if (child instanceof MapAttributeDefinition) { + processMapAttribute((MapAttributeDefinition) child, objectNode, (Map) value); } else { objectNode.get(child.getName()).set(processObjectAttribute((ObjectTypeAttributeDefinition) child, (Map) value)); } @@ -442,11 +495,11 @@ private void processListAttribute(ListAttributeDefinition att, ModelNode list, O @SuppressWarnings("unchecked") private void processMapAttribute(MapAttributeDefinition att, ModelNode map, Map yaml) { - if(att instanceof ObjectMapAttributeDefinition) { + if (att instanceof ObjectMapAttributeDefinition) { ObjectMapAttributeDefinition objectMapAtt = (ObjectMapAttributeDefinition) att; ModelNode objectMapNode = map.get(att.getName()).setEmptyObject(); for (Map.Entry entry : yaml.entrySet()) { - ModelNode objectValue= processObjectAttribute(objectMapAtt.getValueType(), ( Map) entry.getValue()); + ModelNode objectValue = processObjectAttribute(objectMapAtt.getValueType(), (Map) entry.getValue()); objectMapNode.get(entry.getKey()).set(objectValue); } } else { @@ -472,6 +525,9 @@ public String getCommandLineInstructions() { } private interface Operation { + + String getOperationName(); + void processOperation(ImmutableManagementResourceRegistration rootRegistration, Map xmlOperations, List postExtensionOps, PathAddress address, String name); } @@ -509,6 +565,11 @@ public void processOperation(ImmutableManagementResourceRegistration rootRegistr } } + @Override + public String getOperationName() { + return REMOVE; + } + } private class UndefineOperation implements Operation { @@ -525,9 +586,16 @@ public void processOperation(ImmutableManagementResourceRegistration rootRegistr op.get(OP_ADDR).set(address.toModelNode()); op.get(NAME).set(name); postExtensionOps.add(new ParsedBootOp(op, operationEntry.getOperationHandler())); + } else { + throw MGMT_OP_LOGGER.illegalOperationForAttribute(getOperationName(), name, address.toCLIStyleString()); } } + @Override + public String getOperationName() { + return UNDEFINE_ATTRIBUTE_OPERATION; + } + } private class ListAddOperation implements Operation { @@ -541,9 +609,12 @@ private class ListAddOperation implements Operation { @Override @SuppressWarnings("unchecked") public void processOperation(ImmutableManagementResourceRegistration rootRegistration, Map xmlOperations, List postExtensionOps, PathAddress address, String name) { - OperationEntry operationEntry = rootRegistration.getOperationEntry(address, "list-add"); + OperationEntry operationEntry = rootRegistration.getOperationEntry(address, getOperationName()); if (operationEntry != null) { AttributeAccess access = rootRegistration.getAttributeAccess(address, name); + if (!(access.getAttributeDefinition() instanceof ListAttributeDefinition)) { + throw MGMT_OP_LOGGER.illegalOperationForAttribute(getOperationName(), name, address.toCLIStyleString()); + } ListAttributeDefinition att = (ListAttributeDefinition) access.getAttributeDefinition(); AttributeDefinition type = att.getValueAttributeDefinition(); if (type == null) { @@ -613,6 +684,11 @@ public void processOperation(ImmutableManagementResourceRegistration rootRegistr } } + @Override + public String getOperationName() { + return "list-add"; + } + } private class OperationConstructor extends Constructor { diff --git a/controller/src/test/java/org/jboss/as/controller/persistence/yaml/YamlConfigurationExtensionTest.java b/controller/src/test/java/org/jboss/as/controller/persistence/yaml/YamlConfigurationExtensionTest.java index 29b16186445..c5b1fbf4cd9 100644 --- a/controller/src/test/java/org/jboss/as/controller/persistence/yaml/YamlConfigurationExtensionTest.java +++ b/controller/src/test/java/org/jboss/as/controller/persistence/yaml/YamlConfigurationExtensionTest.java @@ -264,7 +264,7 @@ public void testUnknownResource() throws URISyntaxException { instance.processOperations(rootRegistration, postExtensionOps); fail("Unknown resource should make the yaml extension fail"); } catch (java.lang.IllegalArgumentException ex) { - assertEquals("WFLYCTL0502: No child resource called system-propety could be found at address /'.", ex.getMessage()); + assertEquals("WFLYCTL0502: No child resource called 'system-propety' could be found at address '/'.", ex.getMessage()); } } @@ -299,7 +299,7 @@ public void testUnknownChildResource() throws URISyntaxException { try { instance.processOperations(rootRegistration, postExtensionOps);fail("Unknown resource should make the yaml extension fail"); } catch (java.lang.IllegalArgumentException ex) { - assertEquals("WFLYCTL0502: No child resource called children could be found at address /parent=homer'.", ex.getMessage()); + assertEquals("WFLYCTL0502: No child resource called 'children' could be found at address '/parent=homer'.", ex.getMessage()); } } 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/launcher/src/main/java/org/wildfly/core/launcher/BootableJarCommandBuilder.java b/launcher/src/main/java/org/wildfly/core/launcher/BootableJarCommandBuilder.java index 9eaaef962f6..c9c4704cf86 100644 --- a/launcher/src/main/java/org/wildfly/core/launcher/BootableJarCommandBuilder.java +++ b/launcher/src/main/java/org/wildfly/core/launcher/BootableJarCommandBuilder.java @@ -5,11 +5,15 @@ package org.wildfly.core.launcher; +import java.io.File; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.StringJoiner; + import static org.wildfly.core.launcher.JBossModulesCommandBuilder.DEFAULT_VM_ARGUMENTS; import org.wildfly.core.launcher.Arguments.Argument; @@ -230,6 +234,39 @@ public BootableJarCommandBuilder addJavaOption(final String jvmArg) { return this; } + /** + * Adds the YAML configuration file argument with the given YAML configuration files. + * + * @param yamlFiles the files to add + * + * @return the builder + */ + public BootableJarCommandBuilder setYamlFiles(final Path... yamlFiles) { + if (yamlFiles == null || yamlFiles.length == 0) { + return this; + } + return setYamlFiles(List.of(yamlFiles)); + } + + /** + * Adds the YAML configuration file argument with the given YAML configuration files. + * + * @param yamlFiles the files to add + * + * @return the builder + */ + public BootableJarCommandBuilder setYamlFiles(final Collection yamlFiles) { + if (yamlFiles == null || yamlFiles.isEmpty()) { + return this; + } + StringJoiner joiner = new StringJoiner(File.pathSeparator); + for (Path yamlFile : yamlFiles) { + joiner.add(yamlFile.toAbsolutePath().toString()); + } + setSingleServerArg("--yaml", joiner.toString()); + return this; + } + /** * Adds the array of JVM arguments to the command. * diff --git a/launcher/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java b/launcher/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java index 9c9c360c68e..509cb42a6c9 100644 --- a/launcher/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java +++ b/launcher/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java @@ -138,7 +138,9 @@ public void testBootableJarBuilder() { .addJavaOption("-Djava.security.manager") .addJavaOption("-Djava.net.preferIPv4Stack=true") .addJavaOption("-Djava.net.preferIPv4Stack=false") - .setBindAddressHint("management", "0.0.0.0"); + .setBindAddressHint("management", "0.0.0.0") + .setYamlFiles(Path.of("bad.yml")) + .setYamlFiles(Path.of("dummy.yml")); // Get all the commands List commands = commandBuilder.buildArguments(); @@ -151,6 +153,8 @@ public void testBootableJarBuilder() { Assert.assertTrue("Missing debug argument", commands.contains(String.format(StandaloneCommandBuilder.DEBUG_FORMAT, "y", 5005))); + Assert.assertTrue("--yaml is missing", commands.contains("--yaml=" + Path.of("dummy.yml").toFile().getAbsolutePath())); + // If we're using Java 12+. the enhanced security manager option must be set. testEnhancedSecurityManager(commands, 1); // Bootable JAR handles JPMS arguments thanks to its Manifest file. @@ -173,6 +177,15 @@ public void testBootableJarBuilder() { } Assert.assertEquals("There should be only one --install-dir", 1, count); + // Install dir should be added once. + count = 0L; + for (String s : commandBuilder.getServerArguments()) { + if (s.contains("--yaml")) { + count++; + } + } + Assert.assertEquals("There should be only one --yaml", 1, count); + // Rename the binding address commandBuilder.setBindAddressHint(null); commands = commandBuilder.buildArguments(); diff --git a/model-test/src/main/java/org/jboss/as/model/test/StringConfigurationPersister.java b/model-test/src/main/java/org/jboss/as/model/test/StringConfigurationPersister.java index bda6d3dc17f..ed521a8534c 100644 --- a/model-test/src/main/java/org/jboss/as/model/test/StringConfigurationPersister.java +++ b/model-test/src/main/java/org/jboss/as/model/test/StringConfigurationPersister.java @@ -27,6 +27,7 @@ public class StringConfigurationPersister extends AbstractConfigurationPersister private final List bootOperations; private final boolean persistXml; volatile String marshalled; + private volatile boolean stored = false; public StringConfigurationPersister(List bootOperations, XMLElementWriter rootDeparser, boolean persistXml) { super(rootDeparser); @@ -40,6 +41,7 @@ public PersistenceResource store(ModelNode model, Set affectedAddre if (!persistXml) { return new NullConfigurationPersister().store(model, affectedAddresses); } + stored = true; return new StringPersistenceResource(model, this); } @@ -56,6 +58,11 @@ public String getMarshalled() { return marshalled; } + @Override + public boolean hasStored() { + return isPersisting() && stored; + } + private class StringPersistenceResource implements PersistenceResource { private byte[] bytes; 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 da07ab4e31e..589684470e6 100644 --- a/server/src/main/java/org/jboss/as/server/ServerEnvironment.java +++ b/server/src/main/java/org/jboss/as/server/ServerEnvironment.java @@ -350,7 +350,7 @@ public ServerEnvironment(final String hostControllerName, final Properties props Path[] supplementalConfigurationFiles = findSupplementalConfigurationFiles(null, supplementalConfiguration); ConfigurationExtension configurationExtension = ConfigurationExtensionFactory.createConfigurationExtension(supplementalConfigurationFiles); if (configurationExtension != null) { - configInteractionPolicy = configurationExtension.shouldProcessOperations(initialRunningMode) ? ConfigurationFile.InteractionPolicy.READ_ONLY : configInteractionPolicy; + configInteractionPolicy = configurationExtension.shouldProcessOperations(runningModeControl) ? ConfigurationFile.InteractionPolicy.READ_ONLY : configInteractionPolicy; } homeDir = new File(WildFlySecurityManager.getPropertyPrivileged("user.dir", ".")); serverBaseDir = new File(WildFlySecurityManager.getPropertyPrivileged("user.dir", ".")); @@ -415,7 +415,7 @@ public ServerEnvironment(final String hostControllerName, final Properties props Path[] supplementalConfigurationFiles = findSupplementalConfigurationFiles(serverConfigurationDir.toPath(), supplementalConfiguration); ConfigurationExtension configurationExtension = ConfigurationExtensionFactory.createConfigurationExtension(supplementalConfigurationFiles); if (configurationExtension != null) { - configInteractionPolicy = configurationExtension.shouldProcessOperations(initialRunningMode) ? ConfigurationFile.InteractionPolicy.READ_ONLY : configInteractionPolicy; + configInteractionPolicy = configurationExtension.shouldProcessOperations(runningModeControl) ? ConfigurationFile.InteractionPolicy.READ_ONLY : configInteractionPolicy; } tmp = getFileFromProperty(SERVER_DATA_DIR, props); diff --git a/server/src/main/java/org/jboss/as/server/controller/resources/ServerRootResourceDefinition.java b/server/src/main/java/org/jboss/as/server/controller/resources/ServerRootResourceDefinition.java index 8135be4c5f9..1748a356f69 100644 --- a/server/src/main/java/org/jboss/as/server/controller/resources/ServerRootResourceDefinition.java +++ b/server/src/main/java/org/jboss/as/server/controller/resources/ServerRootResourceDefinition.java @@ -414,8 +414,9 @@ public void registerOperations(ManagementResourceRegistration resourceRegistrati } else { - ServerProcessReloadHandler.registerStandardReloadOperation(resourceRegistration, runningModeControl, processState, serverEnvironment); - ServerProcessReloadHandler.registerEnhancedReloadOperation(resourceRegistration, runningModeControl, processState, serverEnvironment); + + ServerProcessReloadHandler.registerStandardReloadOperation(resourceRegistration, runningModeControl, processState, serverEnvironment, extensibleConfigurationPersister); + ServerProcessReloadHandler.registerEnhancedReloadOperation(resourceRegistration, runningModeControl, processState, serverEnvironment, extensibleConfigurationPersister); resourceRegistration.registerOperationHandler(ServerSuspendHandler.DEFINITION, ServerSuspendHandler.INSTANCE); resourceRegistration.registerOperationHandler(ServerResumeHandler.DEFINITION, ServerResumeHandler.INSTANCE); diff --git a/server/src/main/java/org/jboss/as/server/operations/ServerDomainProcessReloadHandler.java b/server/src/main/java/org/jboss/as/server/operations/ServerDomainProcessReloadHandler.java index e9151d7f24f..e5031c5f5d5 100644 --- a/server/src/main/java/org/jboss/as/server/operations/ServerDomainProcessReloadHandler.java +++ b/server/src/main/java/org/jboss/as/server/operations/ServerDomainProcessReloadHandler.java @@ -26,7 +26,7 @@ public class ServerDomainProcessReloadHandler extends ServerProcessReloadHandler public ServerDomainProcessReloadHandler(ServiceName rootService, RunningModeControl runningModeControl, ControlledProcessState processState, final DomainServerCommunicationServices.OperationIDUpdater operationIDUpdater, final ServerEnvironment serverEnvironment) { - super(rootService, runningModeControl, processState, serverEnvironment); + super(rootService, runningModeControl, processState, serverEnvironment, null, null); this.operationIDUpdater = operationIDUpdater; } diff --git a/server/src/main/java/org/jboss/as/server/operations/ServerProcessReloadHandler.java b/server/src/main/java/org/jboss/as/server/operations/ServerProcessReloadHandler.java index a57c2af6d18..28f2d969d20 100644 --- a/server/src/main/java/org/jboss/as/server/operations/ServerProcessReloadHandler.java +++ b/server/src/main/java/org/jboss/as/server/operations/ServerProcessReloadHandler.java @@ -25,6 +25,7 @@ import org.jboss.as.controller.operations.common.ProcessReloadHandler; import org.jboss.as.controller.operations.validation.EnumValidator; import org.jboss.as.controller.registry.ManagementResourceRegistration; +import org.jboss.as.controller.persistence.ExtensibleConfigurationPersister; import org.jboss.as.server.ServerEnvironment; import org.jboss.as.server.Services; import org.jboss.as.server.controller.descriptions.ServerDescriptions; @@ -84,24 +85,23 @@ public class ServerProcessReloadHandler extends ProcessReloadHandler additionalAttributes; private final ServerEnvironment environment; + private ExtensibleConfigurationPersister extensibleConfigurationPersister; - protected ServerProcessReloadHandler(ServiceName rootService, RunningModeControl runningModeControl, ControlledProcessState processState, ServerEnvironment environment) { - this(rootService, runningModeControl, processState, environment, null); - } - - private ServerProcessReloadHandler(ServiceName rootService, RunningModeControl runningModeControl, ControlledProcessState processState, ServerEnvironment environment, Set additionalAttributes) { + public ServerProcessReloadHandler(ServiceName rootService, RunningModeControl runningModeControl, + ControlledProcessState processState, ServerEnvironment environment, Set additionalAttributes, ExtensibleConfigurationPersister extensibleConfigurationPersister) { super(rootService, runningModeControl, processState); this.additionalAttributes = additionalAttributes == null ? Collections.emptySet() : additionalAttributes; this.environment = environment; + this.extensibleConfigurationPersister = extensibleConfigurationPersister; } - public static void registerStandardReloadOperation(ManagementResourceRegistration resourceRegistration, RunningModeControl runningModeControl, ControlledProcessState processState, ServerEnvironment serverEnvironment) { - ServerProcessReloadHandler reloadHandler = new ServerProcessReloadHandler(Services.JBOSS_AS, runningModeControl, processState, serverEnvironment); + public static void registerStandardReloadOperation(ManagementResourceRegistration resourceRegistration, RunningModeControl runningModeControl, ControlledProcessState processState, ServerEnvironment serverEnvironment, ExtensibleConfigurationPersister extensibleConfigurationPersister) { + ServerProcessReloadHandler reloadHandler = new ServerProcessReloadHandler(Services.JBOSS_AS, runningModeControl, processState, serverEnvironment, null, extensibleConfigurationPersister); resourceRegistration.registerOperationHandler(ServerProcessReloadHandler.STANDARD_DEFINITION, reloadHandler, false); } - public static void registerEnhancedReloadOperation(ManagementResourceRegistration resourceRegistration, RunningModeControl runningModeControl, ControlledProcessState processState, ServerEnvironment serverEnvironment) { - ServerProcessReloadHandler reloadHandler = new ServerProcessReloadHandler(Services.JBOSS_AS, runningModeControl, processState, serverEnvironment, getAttributeNames(ENHANCED_ATTRIBUTES)); + public static void registerEnhancedReloadOperation(ManagementResourceRegistration resourceRegistration, RunningModeControl runningModeControl, ControlledProcessState processState, ServerEnvironment serverEnvironment, ExtensibleConfigurationPersister extensibleConfigurationPersister) { + ServerProcessReloadHandler reloadHandler = new ServerProcessReloadHandler(Services.JBOSS_AS, runningModeControl, processState, serverEnvironment, getAttributeNames(ENHANCED_ATTRIBUTES), extensibleConfigurationPersister); resourceRegistration.registerOperationHandler(ServerProcessReloadHandler.ENHANCED_DEFINITION, reloadHandler, false); } @@ -149,6 +149,13 @@ protected ProcessReloadHandler.ReloadContext initializeReloa final boolean finalSuspend = suspend; final boolean finalAdminOnly = adminOnly; + //We need to know if some changes were applied because then the resulting standalone-boot.xml would contain + //the configuration extension changes thus we must not re-apply them. But if there are changes due to + // a boot cli script tor if there has been no changes persisted then we must apply the configuration + // extension changes. + final boolean applyConfigurationExtension = !(context.isNormalServer() || finalAdminOnly) || + (extensibleConfigurationPersister != null && !extensibleConfigurationPersister.hasStored()); + final String serverConfig = unmanaged && operation.hasDefined(SERVER_CONFIG.getName()) ? SERVER_CONFIG.resolveModelAttribute(context, operation).asString() : null; if (operation.hasDefined(USE_CURRENT_SERVER_CONFIG.getName()) && serverConfig != null) { @@ -170,10 +177,10 @@ public void doReload(RunningModeControl runningModeControl) { runningModeControl.setUseCurrentConfig(useCurrentConfig); runningModeControl.setNewBootFileName(serverConfig); runningModeControl.setSuspend(finalSuspend); - if (stability != null) { environment.setStability(stability); } + runningModeControl.setApplyConfigurationExtension(applyConfigurationExtension); } }; } 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 deleted file mode 100644 index ddbd139334a..00000000000 --- a/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/persistence/YamlExtensionTestCase.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright The WildFly Authors - * SPDX-License-Identifier: Apache-2.0 - */ -package org.jboss.as.test.manualmode.management.persistence; - -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; -import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNNING_MODE; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import jakarta.inject.Inject; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.Iterator; -import java.util.regex.Pattern; -import org.jboss.as.controller.PathAddress; -import org.jboss.as.controller.client.ModelControllerClient; -import org.jboss.as.controller.client.helpers.Operations; -import org.jboss.as.controller.client.impl.AdditionalBootCliScriptInvoker; -import org.jboss.as.controller.operations.common.Util; -import org.jboss.as.test.integration.management.util.CLIWrapper; -import org.jboss.as.test.shared.TimeoutUtil; -import org.jboss.dmr.ModelNode; -import org.junit.After; -import org.junit.Assert; -import org.junit.Assume; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.wildfly.core.testrunner.Server; -import org.wildfly.core.testrunner.ServerControl; -import org.wildfly.core.testrunner.ServerController; -import org.wildfly.core.testrunner.WildFlyRunner; -import org.wildfly.security.manager.WildFlySecurityManager; - -/** - * Simple test to check that we can apply YAML configuration over an existing standard configuration. - * Checking that reloading doesn't break the resulting configuration. - * Testing the cli ops are compatible with the YAML changes. - * - * @author Emmanuel Hugonnet (c) 2021 Red Hat, Inc. - */ -@RunWith(WildFlyRunner.class) -@ServerControl(manual = true) -public class YamlExtensionTestCase { - - private static final ModelNode READ_CONFIG = Util.createEmptyOperation("read-config-as-xml", PathAddress.EMPTY_ADDRESS); - - @Inject - private ServerController container; - - private static Path markerDirectory; - private static Path testYaml; - private static Path cliScript; - private static String expectedXml; - private static String expectedBootCLiXml; - private static String originalJvmArgs; - - @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 referenceConfiguration = configurationDir.resolve("reference-standalone.xml"); - Files.copy(configurationDir.resolve("standalone.xml"), referenceConfiguration, REPLACE_EXISTING); - try (CLIWrapper cli = new CLIWrapper(false)) { - cli.sendLine("embed-server --admin-only --server-config=reference-standalone.xml"); - cli.sendLine("/socket-binding-group=standard-sockets/socket-binding=http:add(interface=public)"); - cli.sendLine("/socket-binding-group=standard-sockets/socket-binding=https:add()"); - cli.sendLine("/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=mail-snmt:add(host=foo, port=8081)"); - cli.sendLine("/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=foo2:add(host=foo2, port=8082)"); - cli.quit(); - } - 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); - 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)"); - cli.sendLine("/subsystem=elytron/properties-realm=bootable-realm:add(users-properties={path=bootable-users.properties, plain-text=true, relative-to=jboss.server.config.dir}, groups-properties={path=bootable-groups.properties, relative-to=jboss.server.config.dir})"); - cli.sendLine("/subsystem=elytron/security-domain=BootableDomain:add(default-realm=bootable-realm, permission-mapper=default-permission-mapper, realms=[{realm=bootable-realm, role-decoder=groups-to-roles}])"); - cli.sendLine("/socket-binding-group=standard-sockets/socket-binding=http:add(interface=public)"); - cli.sendLine("/socket-binding-group=standard-sockets/socket-binding=https:add()"); - cli.sendLine("/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=mail-snmt:add(host=foo, port=8081)"); - cli.sendLine("/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=foo2:add(host=foo2, port=8082)"); - cli.quit(); - } - testYaml = new File(YamlExtensionTestCase.class.getResource("test.yml").toURI()).toPath().toAbsolutePath(); - cliScript = new File(YamlExtensionTestCase.class.getResource("test.cli").toURI()).toPath().toAbsolutePath(); - expectedXml = loadFile(referenceConfiguration).replace("\"", "'"); - expectedBootCLiXml = loadFile(referenceCliConfiguration).replace("\"", "'"); - originalJvmArgs = WildFlySecurityManager.getPropertyPrivileged("jvm.args", null); - Path target = new File("target").toPath(); - markerDirectory = Files.createDirectories(target.resolve("yaml").resolve("cli-boot-ops")); - Files.copy(new File(YamlExtensionTestCase.class.getResource("bootable-groups.properties").toURI()).toPath(), - target.resolve(WildFlySecurityManager.getPropertyPrivileged("jboss.home", "toto")).resolve("standalone").resolve("configuration").resolve("bootable-groups.properties"), REPLACE_EXISTING); - Files.copy(new File(YamlExtensionTestCase.class.getResource("bootable-users.properties").toURI()).toPath(), - target.resolve(WildFlySecurityManager.getPropertyPrivileged("jboss.home", "toto")).resolve("standalone").resolve("configuration").resolve("bootable-users.properties"), REPLACE_EXISTING); - } - - private static String loadFile(Path file) throws IOException { - Iterator iter = Files.readAllLines(file).iterator(); - StringBuilder builder = new StringBuilder(); - while (iter.hasNext()) { - String cleanLine = removeWhiteSpaces(iter.next()); - if (!cleanLine.isBlank()) { - builder.append(cleanLine); - if (iter.hasNext()) { - builder.append("\n"); - } - } - } - return builder.toString(); - } - - @After - public void tearDown() { - if (container.isStarted()) { - container.stop(true); - } - if (originalJvmArgs != null) { - WildFlySecurityManager.setPropertyPrivileged("jvm.args", originalJvmArgs); - } else { - WildFlySecurityManager.clearPropertyPrivileged("jvm.args"); - } - } - - @Test - public void testSimpleYaml() throws URISyntaxException, Exception { - try { - container.startYamlExtension(new Path[]{testYaml}); - String xml = readXmlConfig(); - Assert.assertEquals(expectedXml, xml); - } finally { - container.stop(); - } - } - - @Test - public void testSimpleYamlWithReload() throws URISyntaxException, Exception { - try { - container.startYamlExtension(new Path[]{testYaml}); - String xml = readXmlConfig(); - Assert.assertEquals(expectedXml, xml); - container.waitForLiveServerToReload(TimeoutUtil.adjust(5000)); - xml = readXmlConfig(); - compareXML(expectedXml, xml); - } finally { - container.stop(); - } - } - - @Test - public void testSimpleYamlWithCliBootOps() throws URISyntaxException, Exception { - try { - StringBuilder sb = new StringBuilder(); - sb.append(" -D" + AdditionalBootCliScriptInvoker.MARKER_DIRECTORY_PROPERTY + "=").append(markerDirectory.toAbsolutePath()); - sb.append(" -D" + AdditionalBootCliScriptInvoker.CLI_SCRIPT_PROPERTY + "=").append(cliScript.toAbsolutePath()); - // Propagate this property since it has the maven repository information which is needed on CI - if (WildFlySecurityManager.getPropertyPrivileged("cli.jvm.args", null) != null) { - sb.append(" ").append(WildFlySecurityManager.getPropertyPrivileged("cli.jvm.args", null)); - } - WildFlySecurityManager.setPropertyPrivileged("jvm.args", sb.toString()); - container.start(null, null, Server.StartMode.ADMIN_ONLY, System.out, false, null, null, null, null, new Path[]{testYaml}); - container.waitForLiveServerToReload(TimeoutUtil.adjust(5000)); - waitForRunningMode("NORMAL"); - String xml = readXmlConfig(); - compareXML(expectedBootCLiXml, xml); - } finally { - container.stop(); - } - } - - private void waitForRunningMode(String runningMode) throws Exception { - // Following a reload to normal mode, we might read the running mode too early and hit the admin-only server - // Cycle around a bit to make sure we get the server reloaded into normal mode - long end = System.currentTimeMillis() + TimeoutUtil.adjust(10000); - while (true) { - try { - Thread.sleep(100); - Assert.assertEquals(runningMode, getRunningMode()); - break; - } catch (Throwable e) { - if (System.currentTimeMillis() >= end) { - throw e; - } - } - } - } - - String getRunningMode() throws Exception { - ModelNode op = Util.getReadAttributeOperation(PathAddress.EMPTY_ADDRESS, RUNNING_MODE); - ModelNode result = container.getClient().executeForResult(op); - return result.asString(); - } - - private void compareXML(String expected, String result) { - String[] expectedLines = expected.split(System.lineSeparator()); - String[] resultLines = result.split(System.lineSeparator()); - for (int i = 0; i < expectedLines.length; i++) { - if (i < resultLines.length) { - Assert.assertEquals("Expected " + expectedLines[i] + " but got " + resultLines[i] + " in " + System.lineSeparator() + result, removeWhiteSpaces(expectedLines[i]), removeWhiteSpaces(resultLines[i])); - } else { - Assert.fail("Missing line " + expectedLines[i] + " in " + System.lineSeparator() + result); - } - } - - } - - private String readXmlConfig() throws IOException { - try (ModelControllerClient client = container.getClient().getControllerClient()) { - return removeWhiteSpaces(Operations.readResult(client.execute(READ_CONFIG)).asString().replace("\\\"", "\"").replace("\r\n", "\n")); - } - } - - 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/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 new file mode 100644 index 00000000000..7094d2f656f --- /dev/null +++ b/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/persistence/yaml/YamlExtensionTestCase.java @@ -0,0 +1,645 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ +package org.jboss.as.test.manualmode.management.persistence.yaml; + +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME; +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.SUCCESS; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.UUID; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import jakarta.inject.Inject; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Iterator; +import java.util.regex.Pattern; + +import org.apache.commons.io.IOUtils; +import org.hamcrest.CoreMatchers; +import org.jboss.as.controller.PathAddress; +import org.jboss.as.controller.client.ModelControllerClient; +import org.jboss.as.controller.client.Operation; +import org.jboss.as.controller.client.OperationMessageHandler; +import org.jboss.as.controller.client.OperationResponse; +import org.jboss.as.controller.client.helpers.Operations; +import org.jboss.as.controller.client.impl.AdditionalBootCliScriptInvoker; +import org.jboss.as.controller.operations.common.Util; +import org.jboss.as.test.integration.management.util.CLIWrapper; +import org.jboss.as.test.shared.TimeoutUtil; +import org.jboss.dmr.ModelNode; +import org.jboss.logging.Logger; +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; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.wildfly.core.testrunner.Server; +import org.wildfly.core.testrunner.ServerControl; +import org.wildfly.core.testrunner.ServerController; +import org.wildfly.core.testrunner.WildFlyRunner; +import org.wildfly.security.manager.WildFlySecurityManager; + +/** + * Simple test to check that we can apply YAML configuration over an existing standard configuration. + * Checking that reloading doesn't break the resulting configuration. + * Testing the cli ops are compatible with the YAML changes. + * + * @author Emmanuel Hugonnet (c) 2021 Red Hat, Inc. + */ +@RunWith(WildFlyRunner.class) +@ServerControl(manual = true) +public class YamlExtensionTestCase { + + private static final Logger log = Logger.getLogger(YamlExtensionTestCase.class); + + private static final ModelNode READ_CONFIG = Util.createEmptyOperation("read-config-as-xml", PathAddress.EMPTY_ADDRESS); + + @Inject + private ServerController container; + + private static final Path jbossHome = System.getProperty("ts.bootable") != null ? Path.of(WildFlySecurityManager.getPropertyPrivileged("basedir", "."), + "target" , "bootable-jar-build-artifacts", "wildfly") : Path.of(WildFlySecurityManager.getPropertyPrivileged("jboss.home", "toto")); + private static final Path basedir = jbossHome.resolve("standalone"); + private static Path markerDirectory; + private static Path testYaml; + private static Path testSocketOverrideYaml; + private static Path testPortOffsetOverrideYaml; + private static Path testRemoveSocketYaml; + private static Path testAddingExtensionPathDeploymentOverlayIgnored; + private static Path testAddingEmptyExtensionFailYaml; + private static Path testDeploymentYaml; + private static Path testManagedDeploymentYaml; + private static Path testReplacingByEmptyResourceYaml; + private static Path testWrongIndentationYaml; + private static Path testNonExistentAttributeYaml; + private static Path testOperationsYaml; + private static Path testUndefineNonExistentAttributeYamlOperations; + private static Path testRemoveAttributeRemovesAboveResourceYamlOperations; + private static Path testRemoveNonExistentResource; + private static Path testListAddOperationToStringFails; + private static Path testListAddOperationToNonExistentResourceFails; + private static Path cliScript; + private static String defaultXml; + private static String expectedXml; + private static String expectedBootCLiXml; + private static String originalJvmArgs; + private static String originalModulePath; + + @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 = basedir.resolve("configuration"); + Path referenceConfiguration = configurationDir.resolve("reference-standalone.xml"); + Files.copy(configurationDir.resolve("standalone.xml"), referenceConfiguration, REPLACE_EXISTING); + + // Provide correct module path for CLIWrapper when running in ts.bootable profile + originalModulePath = WildFlySecurityManager.getPropertyPrivileged("module.path", null); + WildFlySecurityManager.setPropertyPrivileged("module.path", jbossHome.resolve("modules").toString()); + + try (CLIWrapper cli = new CLIWrapper(false)) { + cli.sendLine("embed-server --admin-only --server-config=reference-standalone.xml --jboss-home=" + jbossHome); + cli.sendLine("/socket-binding-group=standard-sockets/socket-binding=http:add(interface=public)"); + cli.sendLine("/socket-binding-group=standard-sockets/socket-binding=https:add()"); + cli.sendLine("/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=mail-snmt:add(host=foo, port=8081)"); + cli.sendLine("/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=foo2:add(host=foo2, port=8082)"); + cli.quit(); + } + Path referenceCliConfiguration = configurationDir.resolve("reference-cli-standalone.xml"); + Files.copy(configurationDir.resolve("standalone.xml"), referenceCliConfiguration, REPLACE_EXISTING); + Files.copy(getResourceFilePath("bootable-groups.properties"), configurationDir.resolve("bootable-groups.properties"), REPLACE_EXISTING); + Files.copy(getResourceFilePath("bootable-users.properties"), 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 --jboss-home=" + jbossHome); + cli.sendLine("/system-property=foo:add(value=bar)"); + cli.sendLine("/subsystem=elytron/properties-realm=bootable-realm:add(users-properties={path=bootable-users.properties, plain-text=true, relative-to=jboss.server.config.dir}, groups-properties={path=bootable-groups.properties, relative-to=jboss.server.config.dir})"); + cli.sendLine("/subsystem=elytron/security-domain=BootableDomain:add(default-realm=bootable-realm, permission-mapper=default-permission-mapper, realms=[{realm=bootable-realm, role-decoder=groups-to-roles}])"); + cli.sendLine("/socket-binding-group=standard-sockets/socket-binding=http:add(interface=public)"); + cli.sendLine("/socket-binding-group=standard-sockets/socket-binding=https:add()"); + cli.sendLine("/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=mail-snmt:add(host=foo, port=8081)"); + cli.sendLine("/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=foo2:add(host=foo2, port=8082)"); + cli.quit(); + } + testYaml = getResourceFilePath("test.yml"); + testSocketOverrideYaml = getResourceFilePath("test-socket-override.yml"); + testPortOffsetOverrideYaml = getResourceFilePath("test-port-offset-override.yml"); + testRemoveSocketYaml = getResourceFilePath("test-remove-socket.yml"); + testAddingExtensionPathDeploymentOverlayIgnored = getResourceFilePath("test-adding-extension-path-deployment-overlay-ignored.yml"); + testAddingEmptyExtensionFailYaml = getResourceFilePath("test-adding-empty-extension.yml"); + testReplacingByEmptyResourceYaml = getResourceFilePath("test-replacing-by-empty-resource.yml"); + testWrongIndentationYaml = getResourceFilePath("test-indentation-wrong.yml"); + testNonExistentAttributeYaml = getResourceFilePath("test-setting-non-existent-attribute.yml"); + testOperationsYaml = getResourceFilePath("test-operations.yml"); + testUndefineNonExistentAttributeYamlOperations = getResourceFilePath("test-undefine-non-existent-attribute.yml"); + testRemoveAttributeRemovesAboveResourceYamlOperations = getResourceFilePath("test-remove-attribute-removes-above-resource.yml"); + 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"); + cliScript = getResourceFilePath("test.cli"); + defaultXml = loadFile(configurationDir.resolve("standalone.xml")).replace("\"", "'"); + expectedXml = loadFile(referenceConfiguration).replace("\"", "'"); + expectedBootCLiXml = loadFile(referenceCliConfiguration).replace("\"", "'"); + originalJvmArgs = WildFlySecurityManager.getPropertyPrivileged("jvm.args", null); + Path target = Path.of("target"); + markerDirectory = Files.createDirectories(target.resolve("yaml").resolve("cli-boot-ops")); + Files.copy(getResourceFilePath("bootable-groups.properties"), + jbossHome.resolve("standalone").resolve("configuration").resolve("bootable-groups.properties"), REPLACE_EXISTING); + Files.copy(getResourceFilePath("bootable-users.properties"), + jbossHome.resolve("standalone").resolve("configuration").resolve("bootable-users.properties"), REPLACE_EXISTING); + } + + private static Path getResourceFilePath(String filename) throws URISyntaxException { + return Path.of(YamlExtensionTestCase.class.getResource(filename).toURI()).toAbsolutePath(); + } + + private static String loadFile(Path file) throws IOException { + Iterator iter = Files.readAllLines(file).iterator(); + StringBuilder builder = new StringBuilder(); + while (iter.hasNext()) { + String cleanLine = removeWhiteSpaces(iter.next()); + if (!cleanLine.isBlank()) { + builder.append(cleanLine); + if (iter.hasNext()) { + builder.append("\n"); + } + } + } + return builder.toString(); + } + + @After + public void tearDown() { + if (container.isStarted()) { + container.stop(true); + } + if (originalJvmArgs != null) { + WildFlySecurityManager.setPropertyPrivileged("jvm.args", originalJvmArgs); + } else { + WildFlySecurityManager.clearPropertyPrivileged("jvm.args"); + } + if (originalModulePath != null) { + WildFlySecurityManager.setPropertyPrivileged("module.path", originalModulePath); + } else { + WildFlySecurityManager.clearPropertyPrivileged("module.path"); + } + } + + @Test + public void testSimpleYaml() throws Exception { + container.startYamlExtension(new Path[]{testYaml}); + Assert.assertEquals("Yaml changes to configuration were persisted to xml. This should never happen as it's in read-only mode.", expectedXml, readConfigAsXml()); + // read model and verify that test.yml changes are there + ModelControllerClient client = container.getClient().getControllerClient(); + ModelNode result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("socket-binding-group", "standard-sockets").toModelNode(), true))); + Assert.assertEquals("Yaml change to set default interface is wrong", "public", result.get("default-interface").asString()); + Assert.assertEquals("Yaml change to set port-offset is wrong", "${jboss.socket.binding.port-offset:0}", result.get("port-offset").asString()); + ModelNode outboundSocketBindings = result.get("remote-destination-outbound-socket-binding"); + Assert.assertEquals("Yaml change to port set mail-smtp outbound socket binding is wrong", "foo", outboundSocketBindings.get("mail-snmt").get("host").asString()); + Assert.assertEquals("Yaml change to host set mail-smtp outbound socket binding is wrong", "8081", outboundSocketBindings.get("mail-snmt").get("port").asString()); + Assert.assertEquals("Yaml change to port set foo2 outbound socket binding is wrong", "foo2", outboundSocketBindings.get("foo2").get("host").asString()); + Assert.assertEquals("Yaml change to host set foo2 outbound socket binding is wrong", "8082", outboundSocketBindings.get("foo2").get("port").asString()); + } + + @Test + public void testAddingExtensionPathDeploymentOverlayYamlLogsWarnings() throws Exception { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + container.startYamlExtension(new PrintStream(byteArrayOutputStream), new Path[]{testAddingExtensionPathDeploymentOverlayIgnored}); + + // check no extension added + ModelControllerClient client = container.getClient().getControllerClient(); + Assert.assertEquals("Extension must not be created but it was.", + "failed", client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("extension", "org.jboss.as.failure").toModelNode())).get("outcome").asString()); + // check WARN logged + String consoleOutput = byteArrayOutputStream.toString(); + assertThat("Information that adding extension is ignored is missing in log", consoleOutput, CoreMatchers.containsString("WARN [org.jboss.as.controller.management-operation] (main) WFLYCTL0508: The yaml element 'extension' and its sub-elements are ignored. Thus ignoring element 'org.jboss.as.failure: {module: org.jboss.as.failure}'.")); + assertThat("Information that adding deployment-overlay is ignored is missing in log.", consoleOutput, CoreMatchers.containsString("WARN [org.jboss.as.controller.management-operation] (main) WFLYCTL0508: The yaml element 'deployment-overlay' and its sub-elements are ignored. Thus ignoring element '{dummy-overlay: null}'.")); + assertThat("Information that adding path is ignored is missing in log.", consoleOutput, CoreMatchers.containsString("WARN [org.jboss.as.controller.management-operation] (main) WFLYCTL0508: The yaml element 'path' and its sub-elements are ignored. Thus ignoring element 'test.path: {relative-to: jboss.home.dir, path: bin}'.")); + } + + @Test + public void testEmptyExtensionInYamlLogsWarnings() throws Exception { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + container.startYamlExtension(new PrintStream(byteArrayOutputStream), new Path[]{testAddingEmptyExtensionFailYaml}); + + // check no extension added + ModelControllerClient client = container.getClient().getControllerClient(); + Assert.assertEquals("Extension must not be created but it was.", + "failed", client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("extension", "org.jboss.as.failure").toModelNode())).get("outcome").asString()); + // check WARN logged + 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.")); + } + + + 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 Exception { + container.startYamlExtension(new Path[]{testYaml}); + Assert.assertEquals("Yaml changes to configuration were persisted to xml. This should never happen as it's in read-only mode.", expectedXml, readConfigAsXml()); + container.reload(TimeoutUtil.adjust(5000)); + compareXML(expectedXml, readConfigAsXml()); + } + + @Test + public void testSimpleYamlWithCliBootOps() throws Exception { + Assume.assumeTrue("Boot CLI script can be used on only in admin-only mode which is no valid for bootable jar.", System.getProperty("ts.bootable") == null); + StringBuilder sb = new StringBuilder(); + sb.append(" -D" + AdditionalBootCliScriptInvoker.MARKER_DIRECTORY_PROPERTY + "=").append(markerDirectory.toAbsolutePath()); + sb.append(" -D" + AdditionalBootCliScriptInvoker.CLI_SCRIPT_PROPERTY + "=").append(cliScript.toAbsolutePath()); + // Propagate this property since it has the maven repository information which is needed on CI + if (WildFlySecurityManager.getPropertyPrivileged("cli.jvm.args", null) != null) { + sb.append(" ").append(WildFlySecurityManager.getPropertyPrivileged("cli.jvm.args", null)); + } + WildFlySecurityManager.setPropertyPrivileged("jvm.args", sb.toString()); + container.start(null, null, Server.StartMode.ADMIN_ONLY, System.out, false, null, null, null, null, new Path[]{testYaml}); + container.waitForLiveServerToReload(TimeoutUtil.adjust(5000)); + waitForRunningMode("NORMAL"); + String xml = readConfigAsXml(); + compareXML(expectedBootCLiXml, xml); + } + + @Test + public void testYamlChangesAppliedInAdminOnlyModeWithoutBootCliScript() throws Exception { + container.start(null, null, Server.StartMode.ADMIN_ONLY, System.out, false, null, null, null, null, new Path[]{testYaml}); + Assert.assertEquals("Yaml changes to configuration were persisted to xml. This should never happen as it's in read-only mode.", expectedXml, readConfigAsXml()); + } + + @Test + public void testNoYaml() { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try { + container.startYamlExtension(new PrintStream(byteArrayOutputStream), new Path[]{}); + Assert.fail("Server start must fail if no yaml is specified."); + } catch (Exception ex) { + String output = byteArrayOutputStream.toString(); + Assert.assertTrue("If no yaml is set then server must fail with FATAL error. But there is no such a log entry." + System.lineSeparator() + output, + output.lines().anyMatch( + line -> line.contains("FATAL [org.jboss.as.server] (main) WFLYSRV0239: Aborting with exit code 1") || line.contains("WFLYSRV0072: Value expected for option --yaml"))); + } + } + + @Test + public void testRemoveAndAddingResourceWithOverridingAttributesByTwoYamlFiles() throws Exception { + container.startYamlExtension(new Path[]{testYaml, testRemoveSocketYaml, testSocketOverrideYaml}); + // read model and verify that expected changes are there + ModelControllerClient client = container.getClient().getControllerClient(); + ModelNode result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("socket-binding-group", "standard-sockets").toModelNode(), true))); + Assert.assertEquals("Yaml change to set default interface is wrong", "public", result.get("default-interface").asString()); + Assert.assertEquals("Yaml change to set port-offset is wrong", "${jboss.socket.binding.port-offset:0}", result.get("port-offset").asString()); + ModelNode outboundSocketBindings = result.get("remote-destination-outbound-socket-binding"); + Assert.assertEquals("Yaml change to port set mail-smtp outbound socket binding is wrong", "foo-override", outboundSocketBindings.get("mail-snmt").get("host").asString()); + Assert.assertEquals("Yaml change to host set mail-smtp outbound socket binding is wrong", "8083", outboundSocketBindings.get("mail-snmt").get("port").asString()); + Assert.assertEquals("Yaml change to port set foo2 outbound socket binding is wrong", "foo2-override", outboundSocketBindings.get("foo2").get("host").asString()); + Assert.assertEquals("Yaml change to host set foo2 outbound socket binding is wrong", "8084", outboundSocketBindings.get("foo2").get("port").asString()); + } + + @Test + public void testAddingResourceWithOverridingAttributesByTwoYamlFiles() throws Exception { + container.startYamlExtension(new Path[]{testYaml, testSocketOverrideYaml}); + // read model and verify that expected changes are there + ModelControllerClient client = container.getClient().getControllerClient(); + ModelNode result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("socket-binding-group", "standard-sockets").toModelNode(), true))); + Assert.assertEquals("Yaml change to set default interface is wrong", "public", result.get("default-interface").asString()); + Assert.assertEquals("Yaml change to set port-offset is wrong", "${jboss.socket.binding.port-offset:0}", result.get("port-offset").asString()); + ModelNode outboundSocketBindings = result.get("remote-destination-outbound-socket-binding"); + Assert.assertEquals("Yaml change to port set mail-smtp outbound socket binding is wrong", "foo-override", outboundSocketBindings.get("mail-snmt").get("host").asString()); + Assert.assertEquals("Yaml change to host set mail-smtp outbound socket binding is wrong", "8083", outboundSocketBindings.get("mail-snmt").get("port").asString()); + Assert.assertEquals("Yaml change to port set foo2 outbound socket binding is wrong", "foo2-override", outboundSocketBindings.get("foo2").get("host").asString()); + Assert.assertEquals("Yaml change to host set foo2 outbound socket binding is wrong", "8084", outboundSocketBindings.get("foo2").get("port").asString()); + } + + @Test + public void testAttributeOverrideByTwoYamlFiles() throws Exception { + container.startYamlExtension(new Path[]{testYaml, testPortOffsetOverrideYaml}); + // read model and verify that expected changes are there + ModelControllerClient client = container.getClient().getControllerClient(); + ModelNode result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("socket-binding-group", "standard-sockets").toModelNode(), true))); + Assert.assertEquals("Yaml change to set port-offset is wrong", "0", result.get("port-offset").asString()); + } + + @Test + public void testReplacingResourceByEmptyResourceLogsWarning() { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + container.startYamlExtension(new PrintStream(byteArrayOutputStream), new Path[]{testReplacingByEmptyResourceYaml}); + String expectedConsoleOutput = "WARN [org.jboss.as.controller.management-operation] (Controller Boot Thread) WFLYCTL0490: A YAML resource has been defined for the address /subsystem=logging/periodic-rotating-file-handler=FILE without any attribute. No actions will be taken."; + assertThat("If resource exists and is replaced by empty resource then warning must be logged. But there is none.", byteArrayOutputStream.toString(), CoreMatchers.containsString(expectedConsoleOutput)); + } + + @Test + public void testStartWithBadlyIndentedYaml() { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try { + container.startYamlExtension(new PrintStream(byteArrayOutputStream), new Path[]{testWrongIndentationYaml}); + Assert.fail("Server must not start with badly format yaml."); + } catch (RuntimeException ex) { + Assert.assertFalse("Server must not start with badly format yaml.", container.isStarted()); + String expectedConsoleOutput = "(?s).*mapping values are not allowed here.* in 'reader', line \\d+, column \\d+.*port-offset: \\$\\{jboss\\.socket\\.binding\\.port-of.*"; + Assert.assertTrue("Server must provide useful information where yaml is wrong.", byteArrayOutputStream.toString().matches(expectedConsoleOutput)); + } + } + + @Test + public void testStartWithNonExistentAttributeYaml() { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try { + container.startYamlExtension(new PrintStream(byteArrayOutputStream), new Path[]{testNonExistentAttributeYaml}); + Assert.fail("Server must not start with non-existent attribute."); + } catch (RuntimeException ex) { + Assert.assertFalse("Server must not start with non-existent attribute.", container.isStarted()); + String expectedConsoleOutput = "ERROR [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0055: Caught exception during boot: java.lang.IllegalArgumentException: " + + "WFLYCTL0509: No attribute called 'non-existent-attribute' is defined at address '/socket-binding-group=standard-sockets'."; + assertThat("Server log must contain ERROR with information which attribute is wrong.", byteArrayOutputStream.toString(), CoreMatchers.containsString(expectedConsoleOutput)); + } + } + + @Test + public void testYamlOperations() throws Exception { + container.startYamlExtension(new Path[]{testOperationsYaml}); + ModelControllerClient client = container.getClient().getControllerClient(); + ModelNode result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("socket-binding-group", "standard-sockets").toModelNode(), true))); + Assert.assertEquals("Yaml operation to remove socket binding was not executed. Socket binding is still present.", "undefined", result.get("socket-binding").get("management-https").asString()); + result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("subsystem", "elytron").toModelNode(), true))); + Assert.assertEquals("Yaml operation to undefine disallowed-providers was not executed.", "undefined", result.get("disallowed-providers").asString()); + ModelNode permissions = result.get("permission-set").get("default-permissions").get("permissions"); + Assert.assertEquals("Yaml change to port set mail-smtp outbound socket binding is wrong", "[{\"class-name\" => \"org.wildfly.security.auth.permission.LoginPermission\",\"module\" => \"org.wildfly.security.elytron-base\",\"target-name\" => \"*\"}]", permissions.asString()); + } + + @Test + public void testUndefineNonExistentAttributeYamlOperations() { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try { + container.startYamlExtension(new PrintStream(byteArrayOutputStream), new Path[]{testUndefineNonExistentAttributeYamlOperations}); + Assert.fail("Server must not start with non-existent attribute."); + } catch (RuntimeException ex) { + Assert.assertFalse("Server must not start with non-existent attribute.", container.isStarted()); + String expectedConsoleOutput = "ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) WFLYCTL0013: Operation (\"undefine-attribute\") failed - address: ([\n" + + " (\"socket-binding-group\" => \"standard-sockets\"),\n" + + " (\"socket-binding\" => \"txn-status-manager\")\n" + + "]) - failure description: \"WFLYCTL0201: Unknown attribute 'asfds'\""; + assertThat("Server log must contain ERROR with information which attribute is wrong.", byteArrayOutputStream.toString(), CoreMatchers.containsString(expectedConsoleOutput)); + } + } + + @Test + public void testRemoveAttributeRemovesAboveResourceYamlOperations() throws Exception { + // removing attribute on resource actually removes the whole resource + container.startYamlExtension(new Path[]{testRemoveAttributeRemovesAboveResourceYamlOperations}); + Assert.assertTrue("Server must start.", container.isStarted()); + ModelControllerClient client = container.getClient().getControllerClient(); + ModelNode result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("socket-binding-group", "standard-sockets").toModelNode(), true))); + Assert.assertEquals("Socket binding management-https must be removed.", "undefined", result.get("socket-binding").get("management-https").asString()); + } + + @Test + public void testRemoveNonExistentResource() { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + container.startYamlExtension(new PrintStream(byteArrayOutputStream), new Path[]{testRemoveNonExistentResource}); + Assert.assertTrue("Server must start.", container.isStarted()); + String expectedConsoleOutput = "WARN [org.jboss.as.controller.management-operation] (Controller Boot Thread) WFLYCTL0512: No resource exists at address '/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=non-existent-binding'. Ignoring the remove opreation."; + assertThat("Server log must contain WARN with information what was wrong.", byteArrayOutputStream.toString(), CoreMatchers.containsString(expectedConsoleOutput)); + } + + @Test + public void testListAddOperationToStringFails() { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try { + container.startYamlExtension(new PrintStream(byteArrayOutputStream), new Path[]{testListAddOperationToStringFails}); + Assert.fail("Server must not start."); + } catch (Exception ex) { + String expectedConsoleOutput = "ERROR [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0055: Caught exception during boot: java.lang.IllegalArgumentException: WFLYCTL0510: No operation list-add can be executed for attribute called 'level' is defined at address '/subsystem=logging/root-logger=ROOT'."; + assertThat("Server log must contain ERROR with information what was wrong.", byteArrayOutputStream.toString(), CoreMatchers.containsString(expectedConsoleOutput)); + } + } + + @Test + public void testListAddOperationToNonExistentResourceFails() { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try { + container.startYamlExtension(new PrintStream(byteArrayOutputStream), new Path[]{testListAddOperationToNonExistentResourceFails}); + Assert.fail("Server must not start."); + } catch (Exception ex) { + String expectedConsoleOutput = "ERROR [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0055: Caught exception during boot: java.lang.IllegalArgumentException: WFLYCTL0510: No operation list-add can be executed for attribute called 'NON-EXISTENT' is defined at address '/subsystem=logging/root-logger=NON-EXISTENT'."; + assertThat("Server log must contain ERROR with information what was wrong.", byteArrayOutputStream.toString(), CoreMatchers.containsString(expectedConsoleOutput)); + } + } + + @Test + public void testReadConfigAsXmlFile() throws Exception { + container.startYamlExtension(new Path[]{testYaml}); + ModelNode request = new ModelNode(); + request.get("operation").set("read-config-as-xml-file"); + request.get("address").setEmptyList(); + OperationResponse response = container.getClient().getControllerClient().executeOperation(Operation.Factory.create(request), OperationMessageHandler.DISCARD); + Assert.assertEquals(1, response.getInputStreams().size()); + Assert.assertEquals(SUCCESS, response.getResponseNode().require(OUTCOME).asString()); + String uuid = response.getResponseNode().require(RESULT).require(UUID).asStringOrNull(); + Assert.assertNotNull(uuid); + OperationResponse.StreamEntry stream = response.getInputStream(uuid); + Assert.assertNotNull(stream); + Assert.assertEquals("application/xml", stream.getMimeType()); + String xml; + try (InputStream in = stream.getStream(); ByteArrayOutputStream out = new ByteArrayOutputStream()) { + IOUtils.copy(in, out); + xml = out.toString(StandardCharsets.UTF_8); + } + Path standalone = Paths.get(WildFlySecurityManager.getPropertyPrivileged("jboss.home", "."), "standalone", "configuration", "standalone.xml"); + String expectedXml = new String(Files.readAllBytes(standalone)); + Assert.assertNotEquals("XML configs must not be the same as yaml changes are not persisted." + standalone, expectedXml, xml); + } + + @Test + public void testIdempotence() throws Exception { + container.startYamlExtension(new Path[]{testYaml, testYaml}); + Assert.assertEquals("Yaml changes to configuration were persisted to xml. This should never happen as it's in read-only mode.", expectedXml, readConfigAsXml()); + + ModelControllerClient client = container.getClient().getControllerClient(); + ModelNode result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("socket-binding-group", "standard-sockets").toModelNode(), true))); + Assert.assertEquals("Yaml change to set default interface is wrong", "public", result.get("default-interface").asString()); + Assert.assertEquals("Yaml change to set port-offset is wrong", "${jboss.socket.binding.port-offset:0}", result.get("port-offset").asString()); + ModelNode outboundSocketBindings = result.get("remote-destination-outbound-socket-binding"); + Assert.assertEquals("Yaml change to port set mail-smtp outbound socket binding is wrong", "foo", outboundSocketBindings.get("mail-snmt").get("host").asString()); + Assert.assertEquals("Yaml change to host set mail-smtp outbound socket binding is wrong", "8081", outboundSocketBindings.get("mail-snmt").get("port").asString()); + Assert.assertEquals("Yaml change to port set foo2 outbound socket binding is wrong", "foo2", outboundSocketBindings.get("foo2").get("host").asString()); + Assert.assertEquals("Yaml change to host set foo2 outbound socket binding is wrong", "8082", outboundSocketBindings.get("foo2").get("port").asString()); + } + + @Test + public void testYamlChangesSurviveReload() throws Exception { + container.startYamlExtension(new Path[]{testYaml}); + Assert.assertEquals("Yaml changes to configuration were persisted to xml. This should never happen as it's in read-only mode.", expectedXml, readConfigAsXml()); + // read model and verify that test.yml changes are there + ModelControllerClient client = container.getClient().getControllerClient(); + ModelNode result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("socket-binding-group", "standard-sockets").toModelNode(), true))); + Assert.assertEquals("Yaml change to set default interface is wrong", "public", result.get("default-interface").asString()); + Assert.assertEquals("Yaml change to set port-offset is wrong", "${jboss.socket.binding.port-offset:0}", result.get("port-offset").asString()); + ModelNode outboundSocketBindings = result.get("remote-destination-outbound-socket-binding"); + Assert.assertEquals("Yaml change to port set mail-smtp outbound socket binding is wrong", "foo", outboundSocketBindings.get("mail-snmt").get("host").asString()); + Assert.assertEquals("Yaml change to host set mail-smtp outbound socket binding is wrong", "8081", outboundSocketBindings.get("mail-snmt").get("port").asString()); + Assert.assertEquals("Yaml change to port set foo2 outbound socket binding is wrong", "foo2", outboundSocketBindings.get("foo2").get("host").asString()); + Assert.assertEquals("Yaml change to host set foo2 outbound socket binding is wrong", "8082", outboundSocketBindings.get("foo2").get("port").asString()); + + // reload and check that all changes are still there + container.reload(); + + result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("socket-binding-group", "standard-sockets").toModelNode(), true))); + Assert.assertEquals("Yaml change to set default interface is wrong", "public", result.get("default-interface").asString()); + Assert.assertEquals("Yaml change to set port-offset is wrong", "${jboss.socket.binding.port-offset:0}", result.get("port-offset").asString()); + outboundSocketBindings = result.get("remote-destination-outbound-socket-binding"); + Assert.assertEquals("Yaml change to port set mail-smtp outbound socket binding is wrong", "foo", outboundSocketBindings.get("mail-snmt").get("host").asString()); + Assert.assertEquals("Yaml change to host set mail-smtp outbound socket binding is wrong", "8081", outboundSocketBindings.get("mail-snmt").get("port").asString()); + Assert.assertEquals("Yaml change to port set foo2 outbound socket binding is wrong", "foo2", outboundSocketBindings.get("foo2").get("host").asString()); + Assert.assertEquals("Yaml change to host set foo2 outbound socket binding is wrong", "8082", outboundSocketBindings.get("foo2").get("port").asString()); + // reload and check that all changes are still there + container.reload(Server.StartMode.ADMIN_ONLY); + + result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("socket-binding-group", "standard-sockets").toModelNode(), true))); + Assert.assertEquals("Yaml change to set default interface is wrong", "public", result.get("default-interface").asString()); + Assert.assertEquals("Yaml change to set port-offset is wrong", "${jboss.socket.binding.port-offset:0}", result.get("port-offset").asString()); + outboundSocketBindings = result.get("remote-destination-outbound-socket-binding"); + Assert.assertEquals("Yaml change to port set mail-smtp outbound socket binding is wrong", "foo", outboundSocketBindings.get("mail-snmt").get("host").asString()); + Assert.assertEquals("Yaml change to host set mail-smtp outbound socket binding is wrong", "8081", outboundSocketBindings.get("mail-snmt").get("port").asString()); + Assert.assertEquals("Yaml change to port set foo2 outbound socket binding is wrong", "foo2", outboundSocketBindings.get("foo2").get("host").asString()); + Assert.assertEquals("Yaml change to host set foo2 outbound socket binding is wrong", "8082", outboundSocketBindings.get("foo2").get("port").asString()); + } + + @Test + public void testPostStartCLIChangesToModelSurviveReload() throws Exception { + container.startYamlExtension(new Path[]{testYaml}); + Assert.assertEquals("Yaml changes to configuration were persisted to xml. This should never happen as it's in read-only mode.", expectedXml, readConfigAsXml()); + // read model and verify that test.yml changes are there + ModelControllerClient client = container.getClient().getControllerClient(); + ModelNode result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("socket-binding-group", "standard-sockets").toModelNode(), true))); + Assert.assertEquals("Yaml change to set default interface is wrong", "public", result.get("default-interface").asString()); + Assert.assertEquals("Yaml change to set port-offset is wrong", "${jboss.socket.binding.port-offset:0}", result.get("port-offset").asString()); + ModelNode outboundSocketBindings = result.get("remote-destination-outbound-socket-binding"); + Assert.assertEquals("Yaml change to port set mail-smtp outbound socket binding is wrong", "foo", outboundSocketBindings.get("mail-snmt").get("host").asString()); + Assert.assertEquals("Yaml change to host set mail-smtp outbound socket binding is wrong", "8081", outboundSocketBindings.get("mail-snmt").get("port").asString()); + Assert.assertEquals("Yaml change to port set foo2 outbound socket binding is wrong", "foo2", outboundSocketBindings.get("foo2").get("host").asString()); + Assert.assertEquals("Yaml change to host set foo2 outbound socket binding is wrong", "8082", outboundSocketBindings.get("foo2").get("port").asString()); + + // CLI change to yaml changed part of config + // CLI change to non-yaml changed part of config + try (CLIWrapper cli = new CLIWrapper(true)) { + cli.sendLine("/socket-binding-group=standard-sockets:write-attribute(name=port-offset, value=0)", false); + cli.sendLine("/subsystem=logging/:write-attribute(name=use-deployment-logging-config, value=false)", false); + } + + // reload and check that all changes are still there + container.reload(); + + result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("socket-binding-group", "standard-sockets").toModelNode(), true))); + Assert.assertEquals("Change set to port-offset is wrong", "0", result.get("port-offset").asString()); + result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("subsystem", "logging").toModelNode(), true))); + Assert.assertEquals("Change set to use-deployment-logging-config is wrong", "false", result.get("use-deployment-logging-config").asString()); + } + + + @Test + public void testPostStartCLIChangesToModelDoNotSurviveRestart() throws Exception { + container.startYamlExtension(new Path[]{testYaml}); + Assert.assertEquals("Yaml changes to configuration were persisted to xml. This should never happen as it's in read-only mode.", expectedXml, readConfigAsXml()); + // read model and verify that test.yml changes are there + ModelControllerClient client = container.getClient().getControllerClient(); + ModelNode result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("socket-binding-group", "standard-sockets").toModelNode(), true))); + Assert.assertEquals("Yaml change to set default interface is wrong", "public", result.get("default-interface").asString()); + Assert.assertEquals("Yaml change to set port-offset is wrong", "${jboss.socket.binding.port-offset:0}", result.get("port-offset").asString()); + ModelNode outboundSocketBindings = result.get("remote-destination-outbound-socket-binding"); + Assert.assertEquals("Yaml change to port set mail-smtp outbound socket binding is wrong", "foo", outboundSocketBindings.get("mail-snmt").get("host").asString()); + Assert.assertEquals("Yaml change to host set mail-smtp outbound socket binding is wrong", "8081", outboundSocketBindings.get("mail-snmt").get("port").asString()); + Assert.assertEquals("Yaml change to port set foo2 outbound socket binding is wrong", "foo2", outboundSocketBindings.get("foo2").get("host").asString()); + Assert.assertEquals("Yaml change to host set foo2 outbound socket binding is wrong", "8082", outboundSocketBindings.get("foo2").get("port").asString()); + + try (CLIWrapper cli = new CLIWrapper(true)) { + cli.sendLine("/socket-binding-group=standard-sockets:write-attribute(name=port-offset, value=0)", false); + cli.sendLine("/subsystem=logging/:write-attribute(name=use-deployment-logging-config, value=false)", false); + } + + // restart and check that changes are NOT there + container.stop(); + container.startYamlExtension(new Path[]{testYaml}); + + result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("socket-binding-group", "standard-sockets").toModelNode(), true))); + Assert.assertEquals("Yaml change to set port-offset is wrong", "${jboss.socket.binding.port-offset:0}", result.get("port-offset").asString()); + result = Operations.readResult(client.execute(Operations.createReadResourceOperation(PathAddress.pathAddress("subsystem", "logging").toModelNode(), true))); + Assert.assertEquals("Change set to use-deployment-logging-config is wrong", "true", result.get("use-deployment-logging-config").asString()); + + // restart server without yaml changes and check that xml config did no change + container.stop(); + container.start(); + + Assert.assertEquals("Yaml changes to configuration were persisted to xml. This should never happen.", defaultXml, readConfigAsXml()); + } + + private void waitForRunningMode(String runningMode) throws Exception { + // Following a reload to normal mode, we might read the running mode too early and hit the admin-only server + // Cycle around a bit to make sure we get the server reloaded into normal mode + long end = System.currentTimeMillis() + TimeoutUtil.adjust(10000); + while (true) { + try { + Thread.sleep(100); + Assert.assertEquals(runningMode, getRunningMode()); + break; + } catch (Throwable e) { + if (System.currentTimeMillis() >= end) { + throw e; + } + } + } + } + + String getRunningMode() throws Exception { + ModelNode op = Util.getReadAttributeOperation(PathAddress.EMPTY_ADDRESS, RUNNING_MODE); + ModelNode result = container.getClient().executeForResult(op); + return result.asString(); + } + + private void compareXML(String expected, String result) { + String[] expectedLines = expected.split(System.lineSeparator()); + String[] resultLines = result.split(System.lineSeparator()); + for (int i = 0; i < expectedLines.length; i++) { + if (i < resultLines.length) { + Assert.assertEquals("Expected " + expectedLines[i] + " but got " + resultLines[i] + " in " + System.lineSeparator() + result, removeWhiteSpaces(expectedLines[i]), removeWhiteSpaces(resultLines[i])); + } else { + 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 readConfigAsXml() throws IOException { + ModelControllerClient client = container.getClient().getControllerClient(); + return removeWhiteSpaces(Operations.readResult(client.execute(READ_CONFIG)).asString().replace("\\\"", "\"").replace("\r\n", "\n")); + } + + 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/bootable-groups.properties b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/bootable-groups.properties similarity index 100% rename from testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/bootable-groups.properties rename to testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/bootable-groups.properties diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/bootable-users.properties b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/bootable-users.properties similarity index 100% rename from testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/bootable-users.properties rename to testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/bootable-users.properties diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-adding-empty-extension.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-adding-empty-extension.yml new file mode 100644 index 00000000000..f5e256b3aa2 --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-adding-empty-extension.yml @@ -0,0 +1,3 @@ +--- +wildfly-configuration: + extension: diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-adding-extension-path-deployment-overlay-ignored.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-adding-extension-path-deployment-overlay-ignored.yml new file mode 100644 index 00000000000..91d36891336 --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-adding-extension-path-deployment-overlay-ignored.yml @@ -0,0 +1,11 @@ +--- +wildfly-configuration: + extension: + org.jboss.as.failure: + module: org.jboss.as.failure + deployment-overlay: + dummy-overlay: + path: + test.path: + relative-to: 'jboss.home.dir' + path: bin diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-indentation-wrong.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-indentation-wrong.yml new file mode 100644 index 00000000000..925d1c5f975 --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-indentation-wrong.yml @@ -0,0 +1,6 @@ +--- +wildfly-configuration: + socket-binding-group: + standard-sockets: + default-interface: public + port-offset: ${jboss.socket.binding.port-offset:0} diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-list-add-operation-to-non-existent-resource.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-list-add-operation-to-non-existent-resource.yml new file mode 100644 index 00000000000..05d97dccada --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-list-add-operation-to-non-existent-resource.yml @@ -0,0 +1,7 @@ +--- +wildfly-configuration: + subsystem: + logging: + root-logger: + NON-EXISTENT: !list-add + - level diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-list-add-operation-to-string-fails.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-list-add-operation-to-string-fails.yml new file mode 100644 index 00000000000..180d0708754 --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-list-add-operation-to-string-fails.yml @@ -0,0 +1,8 @@ +--- +wildfly-configuration: + subsystem: + logging: + root-logger: + ROOT: + level: !list-add + - TRACE diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-non-existent-resource.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-non-existent-resource.yml new file mode 100644 index 00000000000..e5bd7d06f58 --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-non-existent-resource.yml @@ -0,0 +1,6 @@ +--- +wildfly-configuration: + socket-binding-group: + standard-sockets: + default-interface: public + non-existent-attribute: blabla 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 new file mode 100644 index 00000000000..4e1d7f7ddbd --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-operations.yml @@ -0,0 +1,17 @@ +--- +wildfly-configuration: + socket-binding-group: + standard-sockets: + socket-binding: + management-https: !remove + + subsystem: + elytron: + disallowed-providers: !undefine + permission-set: + default-permissions: + permissions: !list-add + - class-name: org.wildfly.security.auth.permission.LoginPermission + module: org.wildfly.security.elytron-base + target-name: "*" + index: 0 diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-port-offset-override.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-port-offset-override.yml new file mode 100644 index 00000000000..eff923a30fa --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-port-offset-override.yml @@ -0,0 +1,5 @@ +--- +wildfly-configuration: + socket-binding-group: + standard-sockets: + port-offset: 0 diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-remove-attribute-removes-above-resource.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-remove-attribute-removes-above-resource.yml new file mode 100644 index 00000000000..a580ffdcb1b --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-remove-attribute-removes-above-resource.yml @@ -0,0 +1,7 @@ +--- +wildfly-configuration: + socket-binding-group: + standard-sockets: + socket-binding: + management-https: + port: !remove diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-remove-non-existent-resource.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-remove-non-existent-resource.yml new file mode 100644 index 00000000000..10924dbe96d --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-remove-non-existent-resource.yml @@ -0,0 +1,6 @@ +--- +wildfly-configuration: + socket-binding-group: + standard-sockets: + remote-destination-outbound-socket-binding: + non-existent-binding: !remove diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-remove-socket.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-remove-socket.yml new file mode 100644 index 00000000000..108686c0a3a --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-remove-socket.yml @@ -0,0 +1,6 @@ +--- +wildfly-configuration: + socket-binding-group: + standard-sockets: + remote-destination-outbound-socket-binding: + mail-snmt: !remove diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-replacing-by-empty-resource.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-replacing-by-empty-resource.yml new file mode 100644 index 00000000000..4bb65c130fa --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-replacing-by-empty-resource.yml @@ -0,0 +1,6 @@ +--- +wildfly-configuration: + subsystem: + logging: + periodic-rotating-file-handler: + FILE: diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-setting-non-existent-attribute.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-setting-non-existent-attribute.yml new file mode 100644 index 00000000000..e5bd7d06f58 --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-setting-non-existent-attribute.yml @@ -0,0 +1,6 @@ +--- +wildfly-configuration: + socket-binding-group: + standard-sockets: + default-interface: public + non-existent-attribute: blabla diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-socket-override.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-socket-override.yml new file mode 100644 index 00000000000..9255ea77256 --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-socket-override.yml @@ -0,0 +1,11 @@ +--- +wildfly-configuration: + socket-binding-group: + standard-sockets: + remote-destination-outbound-socket-binding: + mail-snmt: + host: foo-override + port: 8083 + foo2: + host: foo2-override + port: 8084 diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-undefine-non-existent-attribute.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-undefine-non-existent-attribute.yml new file mode 100644 index 00000000000..05dd31c257c --- /dev/null +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test-undefine-non-existent-attribute.yml @@ -0,0 +1,7 @@ +--- +wildfly-configuration: + socket-binding-group: + standard-sockets: + socket-binding: + txn-status-manager: + asfds: !undefine \ No newline at end of file diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/test.cli b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test.cli similarity index 100% rename from testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/test.cli rename to testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test.cli diff --git a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/test.yml b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test.yml similarity index 99% rename from testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/test.yml rename to testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test.yml index 8d9efde8977..37b2c7c76fd 100644 --- a/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/test.yml +++ b/testsuite/manualmode/src/test/resources/org/jboss/as/test/manualmode/management/persistence/yaml/test.yml @@ -1,3 +1,4 @@ +--- wildfly-configuration: extension: org.jboss.as.failure: diff --git a/testsuite/test-runner/src/main/java/org/wildfly/core/testrunner/Server.java b/testsuite/test-runner/src/main/java/org/wildfly/core/testrunner/Server.java index fbf0dd6ebbd..e1b5435368b 100644 --- a/testsuite/test-runner/src/main/java/org/wildfly/core/testrunner/Server.java +++ b/testsuite/test-runner/src/main/java/org/wildfly/core/testrunner/Server.java @@ -207,6 +207,12 @@ protected void start(PrintStream out) { commandBuilder.addJavaOption("-ea") .setBindAddressHint("management", managementAddress); + commandBuilder.setYamlFiles(yamlFiles); + //Forcing this to be able to test failure when no yaml file is provided + if(yamlFiles != null && yamlFiles.length == 0) { + commandBuilder.addServerArgument("--yaml="); + } + if (jbossArgs != null) { String[] args = jbossArgs.split("\\s+"); for (String a : args) { @@ -255,6 +261,10 @@ protected void start(PrintStream out) { } commandBuilder.setYamlFiles(yamlFiles); + //Forcing this to be able to test failure when no yaml file is provided + if(yamlFiles != null && yamlFiles.length == 0) { + commandBuilder.addServerArgument("--yaml="); + } if (configDir != null) { commandBuilder.addJavaOption("-Djboss.server.config.dir="+configDir.toString()); @@ -570,6 +580,8 @@ public void run() { target.write(buf, 0, num); } } catch (IOException ignore) { + System.out.println("***************************************************************"); + ignore.printStackTrace(); } } } diff --git a/testsuite/test-runner/src/main/java/org/wildfly/core/testrunner/ServerController.java b/testsuite/test-runner/src/main/java/org/wildfly/core/testrunner/ServerController.java index 023450c4385..23bf03fd415 100644 --- a/testsuite/test-runner/src/main/java/org/wildfly/core/testrunner/ServerController.java +++ b/testsuite/test-runner/src/main/java/org/wildfly/core/testrunner/ServerController.java @@ -176,7 +176,11 @@ public void startGitBackedConfiguration(final String gitRepository, final String } public void startYamlExtension(final Path[] yamlFiles) { - start(null, null, Server.StartMode.NORMAL, System.out, false, null, null, null, null, yamlFiles); + startYamlExtension(System.out, yamlFiles); + } + + public void startYamlExtension(final PrintStream out, final Path[] yamlFiles) { + start(null, null, Server.StartMode.NORMAL, out, false, null, null, null, null, yamlFiles); } public void stop() {