package org.jboss.as.domain.controller.operations; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.BOOT_TIME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CONTENT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT_OVERLAY; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DOMAIN_MODEL; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ENABLED; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.GROUP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HASH; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HOST; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.IN_SERIES; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.MANAGEMENT_CLIENT_CONTENT; 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.PORT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PROFILE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RELOAD_REQUIRED; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESTART_REQUIRED; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ROLLOUT_PLAN; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ROLLOUT_PLANS; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNNING_SERVER; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNTIME_NAME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVER_CONFIG; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVER_GROUP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SOCKET_BINDING; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SOCKET_BINDING_GROUP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SYSTEM_PROPERTY; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE; import java.io.File; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Executors; import org.jboss.as.controller.AttributeDefinition; import org.jboss.as.controller.BlockingTimeout; import org.jboss.as.controller.BootErrorCollector; import org.jboss.as.controller.CompositeOperationHandler; import org.jboss.as.controller.ControlledProcessState; import org.jboss.as.controller.HashUtil; import org.jboss.as.controller.ManagementModel; import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationDefinition; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.OperationStepHandler; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.ProcessType; import org.jboss.as.controller.ProxyController; import org.jboss.as.controller.RunningMode; import org.jboss.as.controller.RunningModeControl; import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; import org.jboss.as.controller.SimpleOperationDefinitionBuilder; import org.jboss.as.controller.access.management.DelegatingConfigurableAuthorizer; import org.jboss.as.controller.access.management.ManagementSecurityIdentitySupplier; import org.jboss.as.controller.audit.ManagedAuditLogger; import org.jboss.as.controller.client.OperationAttachments; import org.jboss.as.controller.client.OperationMessageHandler; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PRODUCT_NAME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PRODUCT_VERSION; import org.jboss.as.controller.descriptions.NonResolvingResourceDescriptionResolver; import org.jboss.as.controller.extension.ExtensionRegistry; import org.jboss.as.controller.extension.MutableRootResourceRegistrationProvider; import org.jboss.as.controller.extension.RuntimeHostControllerInfoAccessor; import org.jboss.as.controller.operations.common.Util; import org.jboss.as.controller.operations.global.GlobalOperationHandlers; import org.jboss.as.controller.persistence.ExtensibleConfigurationPersister; import org.jboss.as.controller.registry.ManagementResourceRegistration; import org.jboss.as.controller.registry.Resource; import org.jboss.as.controller.services.path.PathManagerService; import org.jboss.as.domain.controller.HostRegistrations; import org.jboss.as.domain.controller.operations.deployment.SyncModelParameters; import org.jboss.as.domain.controller.resources.DomainRootDefinition; import org.jboss.as.domain.management.CoreManagementResourceDefinition; import org.jboss.as.host.controller.HostControllerConfigurationPersister; import org.jboss.as.host.controller.HostPathManagerService; import org.jboss.as.host.controller.HostRunningModeControl; import org.jboss.as.host.controller.RestartMode; import org.jboss.as.host.controller.ServerInventory; import org.jboss.as.host.controller.ignored.IgnoredDomainResourceRegistry; import org.jboss.as.host.controller.mgmt.DomainHostExcludeRegistry; import org.jboss.as.host.controller.mgmt.HostControllerRegistrationHandler; import org.jboss.as.host.controller.mgmt.HostInfo; import org.jboss.as.host.controller.model.host.HostResourceDefinition; import org.jboss.as.host.controller.util.AbstractControllerTestBase; import org.jboss.as.repository.ContentReference; import org.jboss.as.repository.HostFileRepository; import org.jboss.as.server.operations.ServerProcessStateHandler; import org.jboss.as.server.services.security.AbstractVaultReader; import org.jboss.as.version.ProductConfig; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; import org.jboss.vfs.VFS; import org.jboss.vfs.VirtualFile; import org.junit.Assert; import org.junit.Test; /** * Port of ApplyRemoteMasterDomainModelHandler to work with syncing using the operations. * SlaveReconnectTestCase contains other tests relevant to this. If maintaining all the mocks * for this test becomes too cumbersome, they should be ported to SlaveReconnectTestCase in the * domain testsuite. * * @author <a href="mailto:kabir.khan@jboss.com">Kabir Khan</a> */ public class SyncModelServerStateTestCase extends AbstractControllerTestBase { static final AttributeDefinition ATTR = new SimpleAttributeDefinitionBuilder("attr", ModelType.STRING, true).build(); static final OperationDefinition TRIGGER_SYNC = new SimpleOperationDefinitionBuilder("trigger-sync", new NonResolvingResourceDescriptionResolver()) .addParameter(ATTR) .build(); private final ExtensionRegistry extensionRegistry = new ExtensionRegistry(ProcessType.HOST_CONTROLLER, new RunningModeControl(RunningMode.NORMAL), null, null, null, RuntimeHostControllerInfoAccessor.SERVER); private volatile IgnoredDomainResourceRegistry ignoredDomainResourceRegistry; private volatile TestInitializer initializer; private volatile Resource rootResource; private final Map<String, MockServerProxy> serverProxies; private volatile TestSyncRepository repository = new TestSyncRepository(); public SyncModelServerStateTestCase() { super("slave", ProcessType.HOST_CONTROLLER, true); ignoredDomainResourceRegistry = new IgnoredDomainResourceRegistry(hostControllerInfo); serverProxies = new HashMap<>(); serverProxies.put("server-one", new MockServerProxy("server-one")); serverProxies.put("server-two", new MockServerProxy("server-two")); serverProxies.put("server-three", new MockServerProxy("server-three")); } @Override protected DelegatingResourceDefinitionInitializer createInitializer() { initializer = new TestInitializer(); return initializer; } @Override protected void initModel(ManagementModel managementModel) { rootResource = initializer.initModel(managementModel); } private void executeTriggerSyncOperation(Resource rootResource) throws Exception { ReadMasterDomainModelUtil util = ReadMasterDomainModelUtil.readMasterDomainResourcesForInitialConnect(new NoopTransformers(), null, null, rootResource); ModelNode op = Util.createEmptyOperation(TRIGGER_SYNC.getName(), PathAddress.EMPTY_ADDRESS); op.get(DOMAIN_MODEL).set(util.getDescribedResources()); executeForResult(op); } @Test public void testSameModelSync() throws Exception { executeTriggerSyncOperation(rootResource.clone()); for (MockServerProxy proxy : serverProxies.values()) { Assert.assertEquals("running", proxy.state); } } @Test public void testLegacyModelSync() throws Exception { Resource masterRootResource = rootResource.clone(); masterRootResource.getModel().get(PRODUCT_NAME).set("WildFly Core Test"); masterRootResource.getModel().get(PRODUCT_VERSION).set("test 2.0"); Assert.assertFalse(rootResource.getModel().hasDefined(PRODUCT_NAME)); Assert.assertFalse(rootResource.getModel().hasDefined(PRODUCT_VERSION)); executeTriggerSyncOperation(masterRootResource); Assert.assertTrue(rootResource.getModel().hasDefined(PRODUCT_NAME)); Assert.assertEquals("WildFly Core Test", rootResource.getModel().get(PRODUCT_NAME).asString()); Assert.assertTrue(rootResource.getModel().hasDefined(PRODUCT_VERSION)); Assert.assertEquals("test 2.0",rootResource.getModel().get(PRODUCT_VERSION).asString()); } @Test public void testAddDefaultBoottimeSystemProperty() throws Exception { Resource root = rootResource.clone(); Resource prop = Resource.Factory.create(); prop.getModel().get(VALUE).set("123"); //Default is boot-time = true, which indicates a restart is needed root.registerChild(PathElement.pathElement(SYSTEM_PROPERTY, "test"), prop); executeTriggerSyncOperation(root); for (MockServerProxy proxy : serverProxies.values()) { Assert.assertEquals(RESTART_REQUIRED, proxy.state); } } @Test public void testAddBoottimeFalseSystemProperty() throws Exception { Resource root = rootResource.clone(); Resource prop = Resource.Factory.create(); prop.getModel().get(VALUE).set("123"); prop.getModel().get(BOOT_TIME).set(false); root.registerChild(PathElement.pathElement(SYSTEM_PROPERTY, "test"), prop); executeTriggerSyncOperation(root); for (MockServerProxy proxy : serverProxies.values()) { Assert.assertEquals(RELOAD_REQUIRED, proxy.state); } } @Test public void testAddSocketBinding() throws Exception { Resource root = rootResource.clone(); Resource socketBinding = Resource.Factory.create(); socketBinding.getModel().get(PORT).set(1000); Resource socketBindingGroup = root.requireChild(PathElement.pathElement(SOCKET_BINDING_GROUP, "binding-one")); socketBindingGroup.registerChild(PathElement.pathElement(SOCKET_BINDING, "testing"), socketBinding); executeTriggerSyncOperation(root); Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-one").state); Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-two").state); Assert.assertEquals("running", serverProxies.get("server-three").state); } @Test public void testAddDeploymentNoInitialGroups() throws Exception { Resource root = rootResource.clone(); byte[] bytes = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; registerRootDeployment(root, "test.jar", bytes); executeTriggerSyncOperation(root); //No servers should be affected for (MockServerProxy proxy : serverProxies.values()) { Assert.assertEquals("running", proxy.state); } Assert.assertTrue(repository.isEmpty()); //Check deployment exists in model, and that nothing exists in the server group ModelNode model = readResourceRecursive(); checkDeploymentBytes(model, "test.jar", bytes); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-one", DEPLOYMENT)); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-two", DEPLOYMENT)); //Add a reference and sync again registerServerGroupDeployment(root, "group-two", "test.jar"); executeTriggerSyncOperation(root); Assert.assertEquals("running", serverProxies.get("server-one").state); Assert.assertEquals("running", serverProxies.get("server-two").state); Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-three").state); repository.checkAddedReferences(bytes, PathAddress.pathAddress(DEPLOYMENT, "test.jar"), PathAddress.pathAddress(SERVER_GROUP, "group-two").append(DEPLOYMENT, "test.jar")); model = readResourceRecursive(); checkDeploymentBytes(model, "test.jar", bytes); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-one", DEPLOYMENT)); Assert.assertEquals("test.jar", model.get(SERVER_GROUP, "group-two", DEPLOYMENT, "test.jar", RUNTIME_NAME).asString()); } @Test public void testAddDeploymentWithGroups() throws Exception { final Resource root = rootResource.clone(); final byte[] bytes = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; registerRootDeployment(root, "test.jar", bytes); registerServerGroupDeployment(root, "group-one", "test.jar"); executeTriggerSyncOperation(root); Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-one").state); Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-two").state); Assert.assertEquals("running", serverProxies.get("server-three").state); repository.checkAddedReferences(bytes, PathAddress.pathAddress(DEPLOYMENT, "test.jar"), PathAddress.pathAddress(SERVER_GROUP, "group-one").append(DEPLOYMENT, "test.jar")); repository.checkRemovedReferences(null); ModelNode model = readResourceRecursive(); checkDeploymentBytes(model, "test.jar", bytes); Assert.assertEquals("test.jar", model.get(SERVER_GROUP, "group-one", DEPLOYMENT, "test.jar", RUNTIME_NAME).asString()); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-two", DEPLOYMENT)); } @Test public void testUpdateDeployment() throws Exception { final Resource root = rootResource.clone(); byte[] oldBytes = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; registerRootDeployment(root, "test.jar", oldBytes); registerServerGroupDeployment(root, "group-one", "test.jar"); executeTriggerSyncOperation(root); //Don't bother checking here since it is the same as testAddDeploymentWithGroups() reloadServers(); byte[] bytes = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}; replaceRootDeployment(root, "test.jar", bytes); repository.clear(oldBytes); executeTriggerSyncOperation(root); Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-one").state); Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-two").state); Assert.assertEquals("running", serverProxies.get("server-three").state); repository.checkAddedReferences(bytes, PathAddress.pathAddress(DEPLOYMENT, "test.jar")); repository.checkRemovedReferences(oldBytes, PathAddress.pathAddress(DEPLOYMENT, "test.jar")); ModelNode model = readResourceRecursive(); checkDeploymentBytes(model, "test.jar", bytes); Assert.assertEquals("test.jar", model.get(SERVER_GROUP, "group-one", DEPLOYMENT, "test.jar", RUNTIME_NAME).asString()); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-two", DEPLOYMENT)); } @Test public void testRemoveDeployment() throws Exception { final Resource root = rootResource.clone(); byte[] bytes = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; registerRootDeployment(root, "test.jar", bytes); registerServerGroupDeployment(root, "group-one", "test.jar"); executeTriggerSyncOperation(root); //Don't bother checking here since it is the same as testAddDeploymentWithGroups() //Reset the server proxies, simulating a reload for (MockServerProxy proxy : serverProxies.values()) { proxy.state = "running"; } root.removeChild(PathElement.pathElement(DEPLOYMENT, "test.jar")); root.getChild( PathElement.pathElement(SERVER_GROUP, "group-one")).removeChild(PathElement.pathElement(DEPLOYMENT, "test.jar")); repository.clear(bytes); executeTriggerSyncOperation(root); Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-one").state); Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-two").state); Assert.assertEquals("running", serverProxies.get("server-three").state); Assert.assertEquals(2, repository.removedReferences.size()); repository.checkRemovedReferences(bytes, PathAddress.pathAddress(DEPLOYMENT, "test.jar"), PathAddress.pathAddress(SERVER_GROUP, "group-one").append(DEPLOYMENT, "test.jar")); repository.checkAddedReferences(null); //Reset the server proxies, simulating a reload for (MockServerProxy proxy : serverProxies.values()) { proxy.state = "running"; } repository.clear(); ModelNode model = readResourceRecursive(); Assert.assertFalse(model.hasDefined(DEPLOYMENT, "test.jar")); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-one", DEPLOYMENT, "test.jar")); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-two", DEPLOYMENT)); root.removeChild(PathElement.pathElement(DEPLOYMENT, "test.jar")); } @Test public void testRolloutPlans() throws Exception { final Resource root = rootResource.clone(); byte[] originalHash = new byte[]{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; Resource plans = Resource.Factory.create(); plans.getModel().get(HASH).set(originalHash); root.registerChild(PathElement.pathElement(MANAGEMENT_CLIENT_CONTENT, ROLLOUT_PLANS), plans); //Add the first plan ModelNode group1 = new ModelNode(); group1.get(SERVER_GROUP, "group-one"); ModelNode plan = new ModelNode(); plan.get("test", ROLLOUT_PLAN, IN_SERIES).add(group1); repository.addRolloutPlan(plan); try { executeTriggerSyncOperation(root); for (MockServerProxy proxy : serverProxies.values()) { Assert.assertEquals("running", proxy.state); } ModelNode rolloutPlans = readResourceRecursive().get(MANAGEMENT_CLIENT_CONTENT, ROLLOUT_PLANS); Assert.assertArrayEquals(originalHash, rolloutPlans.get(HASH).asBytes()); List<ModelNode> list = rolloutPlans.get(ROLLOUT_PLAN, "test", CONTENT, ROLLOUT_PLAN, IN_SERIES).asList(); Assert.assertEquals(1, list.size()); ModelNode entry = list.get(0); Assert.assertTrue(entry.has(SERVER_GROUP, "group-one")); repository.checkAddedReferences(originalHash, PathAddress.pathAddress(MANAGEMENT_CLIENT_CONTENT, ROLLOUT_PLANS)); repository.checkRemovedReferences(null); } finally { repository.deleteVirtualFileAndParent(); } repository.clear(); //Add another plan byte[] newHash = new byte[]{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; plans.getModel().get(HASH).set(newHash); ModelNode group2 = new ModelNode(); group2.get(SERVER_GROUP, "group-two"); plan.get("test2", ROLLOUT_PLAN, IN_SERIES).add(group2); repository.addRolloutPlan(plan); try { executeTriggerSyncOperation(root); for (MockServerProxy proxy : serverProxies.values()) { Assert.assertEquals("running", proxy.state); } ModelNode rolloutPlans = readResourceRecursive().get(MANAGEMENT_CLIENT_CONTENT, ROLLOUT_PLANS); Assert.assertArrayEquals(newHash, rolloutPlans.get(HASH).asBytes()); List<ModelNode> list = rolloutPlans.get(ROLLOUT_PLAN, "test", CONTENT, ROLLOUT_PLAN, IN_SERIES).asList(); Assert.assertEquals(1, list.size()); ModelNode entry = list.get(0); Assert.assertTrue(entry.has(SERVER_GROUP, "group-one")); list = rolloutPlans.get(ROLLOUT_PLAN, "test2", CONTENT, ROLLOUT_PLAN, IN_SERIES).asList(); Assert.assertEquals(1, list.size()); entry = list.get(0); Assert.assertTrue(entry.has(SERVER_GROUP, "group-two")); repository.checkAddedReferences(newHash, PathAddress.pathAddress(MANAGEMENT_CLIENT_CONTENT, ROLLOUT_PLANS)); repository.checkRemovedReferences(originalHash, PathAddress.pathAddress(MANAGEMENT_CLIENT_CONTENT, ROLLOUT_PLANS)); } finally { repository.deleteVirtualFileAndParent(); } repository.clear(); //Remove all plans plans.getModel().get(HASH).set(new ModelNode()); executeTriggerSyncOperation(root); for (MockServerProxy proxy : serverProxies.values()) { Assert.assertEquals("running", proxy.state); } ModelNode rolloutPlans = readResourceRecursive().get(MANAGEMENT_CLIENT_CONTENT, ROLLOUT_PLANS); Assert.assertFalse(rolloutPlans.hasDefined(HASH)); Assert.assertFalse(rolloutPlans.hasDefined(CONTENT)); } @Test public void testDeploymentOverlayNoGroups() throws Exception { Resource root = rootResource.clone(); byte[] bytes = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; registerRootDeploymentOverlay(root, "test", "/some/path", bytes); //No servers should be affected for (MockServerProxy proxy : serverProxies.values()) { Assert.assertEquals("running", proxy.state); } executeTriggerSyncOperation(root); for (MockServerProxy proxy : serverProxies.values()) { //All servers will be affected https://issues.jboss.org/browse/WFCORE-710 //Assert.assertEquals("running", proxy.state); Assert.assertEquals("reload-required", proxy.state); } repository.checkAddedReferences(bytes, PathAddress.pathAddress(DEPLOYMENT_OVERLAY, "test").append(CONTENT, "/some/path")); //Check deployment-overlay exists in model, and that nothing exists in the server group ModelNode model = readResourceRecursive(); checkDeploymentOverlayBytes(model, "test", "/some/path", bytes); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-one", DEPLOYMENT_OVERLAY)); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-two", DEPLOYMENT_OVERLAY)); //Add a reference and sync again registerServerGroupDeploymentOverlay(root, "group-two", "test", "test.jar"); repository.clear(); reloadServers(); executeTriggerSyncOperation(root); Assert.assertEquals("running", serverProxies.get("server-one").state); Assert.assertEquals("running", serverProxies.get("server-two").state); Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-three").state); Assert.assertTrue(repository.isEmpty()); model = readResourceRecursive(); checkDeploymentOverlayBytes(model, "test", "/some/path", bytes); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-one", DEPLOYMENT_OVERLAY)); Assert.assertTrue(model.hasDefined(SERVER_GROUP, "group-two", DEPLOYMENT_OVERLAY, "test", DEPLOYMENT, "test.jar")); } @Test public void testAddDeploymentOverlayWithGroups() throws Exception { final Resource root = rootResource.clone(); final byte[] bytes = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; registerRootDeploymentOverlay(root, "test", "/some/path", bytes); registerServerGroupDeploymentOverlay(root, "group-two", "test", "test.jar"); executeTriggerSyncOperation(root); //All servers will be affected https://issues.jboss.org/browse/WFCORE-710 //Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-one").state); //Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-two").state); //Assert.assertEquals("running", serverProxies.get("server-three").state); for (MockServerProxy proxy : serverProxies.values()) { //Temp check instead of the above https://issues.jboss.org/browse/WFCORE-710 Assert.assertEquals("reload-required", proxy.state); } repository.checkAddedReferences(bytes, PathAddress.pathAddress(DEPLOYMENT_OVERLAY, "test").append(CONTENT, "/some/path")); repository.checkRemovedReferences(null); ModelNode model = readResourceRecursive(); checkDeploymentOverlayBytes(model, "test", "/some/path", bytes); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-one", DEPLOYMENT_OVERLAY)); Assert.assertTrue(model.hasDefined(SERVER_GROUP, "group-two", DEPLOYMENT_OVERLAY, "test", DEPLOYMENT, "test.jar")); } @Test public void testUpdateDeploymentOverlay() throws Exception { final Resource root = rootResource.clone(); byte[] oldBytes = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; registerRootDeploymentOverlay(root, "test", "/some/path", oldBytes); registerServerGroupDeploymentOverlay(root, "group-two", "test", "test.jar"); executeTriggerSyncOperation(root); //Don't bother checking here since it is the same as testAddDeploymentOverlayWithGroups() reloadServers(); //Do a change replacing the bytes in a content item byte[] bytes = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}; Resource overlay = root.getChild(PathElement.pathElement(DEPLOYMENT_OVERLAY, "test")); Resource content = overlay.getChild(PathElement.pathElement(CONTENT, "/some/path")); content.getModel().get(CONTENT).set(bytes); repository.clear(); executeTriggerSyncOperation(root); //All servers will be affected https://issues.jboss.org/browse/WFCORE-710 //Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-one").state); //Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-two").state); //Assert.assertEquals("running", serverProxies.get("server-three").state); for (MockServerProxy proxy : serverProxies.values()) { //Temp check instead of the above https://issues.jboss.org/browse/WFCORE-710 Assert.assertEquals("reload-required", proxy.state); } repository.checkAddedReferences(bytes, PathAddress.pathAddress(DEPLOYMENT_OVERLAY, "test").append(CONTENT, "/some/path")); repository.checkRemovedReferences(oldBytes, PathAddress.pathAddress(DEPLOYMENT_OVERLAY, "test").append(CONTENT, "/some/path")); ModelNode model = readResourceRecursive(); checkDeploymentOverlayBytes(model, "test", "/some/path", bytes); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-one", DEPLOYMENT_OVERLAY)); Assert.assertTrue(model.hasDefined(SERVER_GROUP, "group-two", DEPLOYMENT_OVERLAY, "test", DEPLOYMENT, "test.jar")); //Do a change adding another content item Resource newContent = Resource.Factory.create(); newContent.getModel().get(CONTENT).set(oldBytes); overlay.registerChild(PathElement.pathElement(CONTENT, "/other/path"), newContent); repository.clear(); executeTriggerSyncOperation(root); //All servers will be affected https://issues.jboss.org/browse/WFCORE-710 //Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-one").state); //Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-two").state); //Assert.assertEquals("running", serverProxies.get("server-three").state); for (MockServerProxy proxy : serverProxies.values()) { //Temp check instead of the above https://issues.jboss.org/browse/WFCORE-710 Assert.assertEquals("reload-required", proxy.state); } repository.checkAddedReferences(oldBytes, PathAddress.pathAddress(DEPLOYMENT_OVERLAY, "test").append(CONTENT, "/other/path")); model = readResourceRecursive(); Map<String, byte[]> contents = new HashMap<>(); contents.put("/some/path", bytes); contents.put("/other/path", oldBytes); checkDeploymentOverlayBytes(model, "test", contents); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-one", DEPLOYMENT_OVERLAY)); Assert.assertTrue(model.hasDefined(SERVER_GROUP, "group-two", DEPLOYMENT_OVERLAY, "test", DEPLOYMENT, "test.jar")); } @Test public void testRemoveDeploymentOverlay() throws Exception { final Resource root = rootResource.clone(); byte[] bytes = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; registerRootDeploymentOverlay(root, "test", "/some/path", bytes); registerServerGroupDeploymentOverlay(root, "group-two", "test", "test.jar"); executeTriggerSyncOperation(root); //Don't bother checking here since it is the same as testAddDeploymentOverlayWithGroups() reloadServers(); repository.clear(); //Remove the resources root.removeChild(PathElement.pathElement(DEPLOYMENT_OVERLAY, "test")); root.getChild(PathElement.pathElement(SERVER_GROUP, "group-two")) .removeChild(PathElement.pathElement(DEPLOYMENT_OVERLAY, "test")); executeTriggerSyncOperation(root); //All servers will be affected https://issues.jboss.org/browse/WFCORE-710 //Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-one").state); //Assert.assertEquals(RELOAD_REQUIRED, serverProxies.get("server-two").state); //Assert.assertEquals("running", serverProxies.get("server-three").state); for (MockServerProxy proxy : serverProxies.values()) { //Temp check instead of the above https://issues.jboss.org/browse/WFCORE-710 Assert.assertEquals("reload-required", proxy.state); } repository.checkAddedReferences(null); repository.checkRemovedReferences(bytes, PathAddress.pathAddress(DEPLOYMENT_OVERLAY, "test").append(CONTENT, "/some/path")); ModelNode model = readResourceRecursive(); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-one", DEPLOYMENT_OVERLAY)); Assert.assertFalse(model.hasDefined(SERVER_GROUP, "group-two", DEPLOYMENT_OVERLAY)); Assert.assertFalse(model.hasDefined(DEPLOYMENT_OVERLAY)); } private void registerRootDeploymentOverlay(Resource root, String name, String path, byte[] bytes) { Resource overlay = Resource.Factory.create(); root.registerChild(PathElement.pathElement(DEPLOYMENT_OVERLAY, name), overlay); Resource content = Resource.Factory.create(); content.getModel().get(CONTENT).set(bytes); overlay.registerChild(PathElement.pathElement(CONTENT, path), content); } private void registerRootDeployment(Resource root, String deploymentName, byte[] bytes) { Resource deployment = Resource.Factory.create(); deployment.getModel().get(NAME).set(deploymentName); deployment.getModel().get(RUNTIME_NAME).set(deploymentName); setDeploymentBytes(deployment, bytes); root.registerChild(PathElement.pathElement(DEPLOYMENT, deploymentName), deployment); } private void replaceRootDeployment(Resource root, String deploymentName, byte[] bytes) { Resource deployment = root.getChild(PathElement.pathElement(DEPLOYMENT, deploymentName)); deployment.getModel().remove(CONTENT); setDeploymentBytes(deployment, bytes); } private void setDeploymentBytes(Resource deployment, byte[] bytes) { ModelNode content = deployment.getModel().get(CONTENT); ModelNode hash = new ModelNode(); hash.get(HASH).set(bytes); content.add(hash); } private void registerServerGroupDeploymentOverlay(Resource root, String groupName, String overlayName, String deploymentName) { Resource group = root.requireChild(PathElement.pathElement(SERVER_GROUP, groupName)); Resource overlay = Resource.Factory.create(); group.registerChild(PathElement.pathElement(DEPLOYMENT_OVERLAY, overlayName), overlay); Resource deployment = Resource.Factory.create(); deployment.getModel().setEmptyObject(); overlay.registerChild(PathElement.pathElement(DEPLOYMENT, deploymentName), deployment); } private void registerServerGroupDeployment(Resource root, String groupName, String deploymentName) { Resource group = root.requireChild(PathElement.pathElement(SERVER_GROUP, groupName)); Resource deployment = Resource.Factory.create(); deployment.getModel().get(ENABLED).set(true); deployment.getModel().get(RUNTIME_NAME).set(deploymentName); group.registerChild(PathElement.pathElement(DEPLOYMENT, deploymentName), deployment); } private void checkDeploymentBytes(ModelNode model, String name, byte[] bytes) { Assert.assertEquals(1, model.get(DEPLOYMENT).keys().size()); ModelNode content = model.get(DEPLOYMENT, name, CONTENT); Assert.assertEquals(1, content.asList().size()); Assert.assertArrayEquals(bytes, content.asList().get(0).get(HASH).asBytes()); } private void checkDeploymentOverlayBytes(ModelNode model, String name, String path, byte[] bytes) { checkDeploymentOverlayBytes(model, name, Collections.singletonMap(path, bytes)); } private void checkDeploymentOverlayBytes(ModelNode model, String name, Map<String, byte[]> contents) { Assert.assertEquals(1, model.get(DEPLOYMENT_OVERLAY).keys().size()); ModelNode overlay = model.get(DEPLOYMENT_OVERLAY, name); Assert.assertEquals(contents.size(), overlay.get(CONTENT).keys().size()); for (Map.Entry<String, byte[]> entry : contents.entrySet()) { ModelNode content = overlay.get(CONTENT, entry.getKey()); Assert.assertArrayEquals(entry.getValue(), content.get(CONTENT).asBytes()); } } private void reloadServers() { //Reset the server proxies, simulating a reload for (MockServerProxy proxy : serverProxies.values()) { proxy.state = "running"; } } private class TestInitializer implements DelegatingResourceDefinitionInitializer { private volatile HostResourceDefinition hostResourceDefinition; private volatile ManagementModel managementModel; private volatile ManagementResourceRegistration hostRegistration; @Override public void setDelegate() { final ExtensibleConfigurationPersister configurationPersister = new EmptyConfigurationPersister(); final boolean isMaster = false; final IgnoredDomainResourceRegistry ignoredDomainResourceRegistry = new IgnoredDomainResourceRegistry(hostControllerInfo); final PathManagerService pathManager = new HostPathManagerService(); final DelegatingConfigurableAuthorizer authorizer = new DelegatingConfigurableAuthorizer(); final ManagementSecurityIdentitySupplier securityIdentitySupplier = new ManagementSecurityIdentitySupplier(); final HostRegistrations hostRegistrations = null; final DomainHostExcludeRegistry domainHostExcludeRegistry = new DomainHostExcludeRegistry(); final MutableRootResourceRegistrationProvider rootResourceRegistrationProvider = new MutableRootResourceRegistrationProvider() { @Override public ManagementResourceRegistration getRootResourceRegistrationForUpdate(OperationContext context) { return managementModel.getRootResourceRegistration(); } }; DomainRootDefinition domain = new DomainRootDefinition(domainController, hostControllerEnvironment, configurationPersister, repository, repository, isMaster, hostControllerInfo, extensionRegistry, ignoredDomainResourceRegistry, pathManager, authorizer, securityIdentitySupplier, hostRegistrations, domainHostExcludeRegistry, rootResourceRegistrationProvider); getDelegatingResourceDefiniton().setDelegate(domain); final String hostName = hostControllerEnvironment.getHostName(); final HostControllerConfigurationPersister hostControllerConfigurationPersister = new HostControllerConfigurationPersister(hostControllerEnvironment, hostControllerInfo, Executors.newCachedThreadPool(), extensionRegistry, extensionRegistry); final HostRunningModeControl runningModeControl = new HostRunningModeControl(RunningMode.NORMAL, RestartMode.SERVERS); final ServerInventory serverInventory = null; final HostFileRepository remoteFileRepository = repository; final AbstractVaultReader vaultReader = null; final ControlledProcessState processState = null; final ManagedAuditLogger auditLogger = null; final BootErrorCollector bootErrorCollector = null; //Save this for later since setDelegate() gets called before initModel.... hostResourceDefinition = new HostResourceDefinition(hostName, hostControllerConfigurationPersister, hostControllerEnvironment, runningModeControl, repository, hostControllerInfo, serverInventory, remoteFileRepository, repository, domainController, extensionRegistry, vaultReader, ignoredDomainResourceRegistry, processState, pathManager, authorizer, securityIdentitySupplier, auditLogger, bootErrorCollector); } protected Resource initModel(final ManagementModel managementModel) { this.managementModel = managementModel; ManagementResourceRegistration rootRegistration = managementModel.getRootResourceRegistration(); //Use the saved hostResourceDefinition hostRegistration = rootRegistration.registerSubModel(hostResourceDefinition); rootRegistration.registerOperationHandler(TRIGGER_SYNC, new TriggerSyncHandler()); GlobalOperationHandlers.registerGlobalOperations(rootRegistration, processType); rootRegistration.registerOperationHandler(CompositeOperationHandler.DEFINITION, CompositeOperationHandler.INSTANCE); Resource rootResource = managementModel.getRootResource(); CoreManagementResourceDefinition.registerDomainResource(rootResource, null); final Resource host = Resource.Factory.create(); final Resource serverOneConfig = Resource.Factory.create(); final ModelNode serverOneModel = new ModelNode(); serverOneModel.get(GROUP).set("group-one"); serverOneModel.get(SOCKET_BINDING_GROUP).set("binding-one"); serverOneConfig.writeModel(serverOneModel); host.registerChild(PathElement.pathElement(SERVER_CONFIG, "server-one"), serverOneConfig); final Resource serverTwoConfig = Resource.Factory.create(); final ModelNode serverTwoModel = new ModelNode(); serverTwoModel.get(GROUP).set("group-one"); serverTwoConfig.writeModel(serverTwoModel); host.registerChild(PathElement.pathElement(SERVER_CONFIG, "server-two"), serverTwoConfig); final Resource serverThreeConfig = Resource.Factory.create(); final ModelNode serverThreeModel = new ModelNode(); serverThreeModel.get(GROUP).set("group-two"); serverThreeConfig.writeModel(serverThreeModel); host.registerChild(PathElement.pathElement(SERVER_CONFIG, "server-three"), serverThreeConfig); rootResource.registerChild(PathElement.pathElement(HOST, hostName), host); final Resource serverGroup1 = Resource.Factory.create(); serverGroup1.getModel().get(PROFILE).set("profile-one"); serverGroup1.getModel().get(SOCKET_BINDING_GROUP).set("binding-one"); rootResource.registerChild(PathElement.pathElement(SERVER_GROUP, "group-one"), serverGroup1); final Resource serverGroup2 = Resource.Factory.create(); serverGroup2.getModel().get(PROFILE).set("profile-two"); serverGroup2.getModel().get(SOCKET_BINDING_GROUP).set("binding-two"); rootResource.registerChild(PathElement.pathElement(SERVER_GROUP, "group-two"), serverGroup2); final Resource profile1 = Resource.Factory.create(); profile1.getModel().setEmptyObject(); rootResource.registerChild(PathElement.pathElement(PROFILE, "profile-one"), profile1); final Resource profile2 = Resource.Factory.create(); profile2.getModel().setEmptyObject(); rootResource.registerChild(PathElement.pathElement(PROFILE, "profile-two"), profile2); final Resource binding1 = Resource.Factory.create(); binding1.getModel().setEmptyObject(); rootResource.registerChild(PathElement.pathElement(SOCKET_BINDING_GROUP, "binding-one"), binding1); final Resource binding2 = Resource.Factory.create(); binding2.getModel().setEmptyObject(); rootResource.registerChild(PathElement.pathElement(SOCKET_BINDING_GROUP, "binding-two"), binding2); registerServer("server-one"); registerServer("server-two"); registerServer("server-three"); return rootResource; } private void registerServer(String serverName) { PathElement pe = PathElement.pathElement(RUNNING_SERVER, serverName); hostRegistration.registerProxyController(pe, serverProxies.get(serverName)); final ManagementResourceRegistration serverRegistration = hostRegistration.getSubModel(PathAddress.EMPTY_ADDRESS.append(pe)); serverRegistration.registerOperationHandler(ServerProcessStateHandler.RELOAD_DEFINITION, serverProxies.get(serverName)); serverRegistration.registerOperationHandler(ServerProcessStateHandler.RESTART_DEFINITION, serverProxies.get(serverName)); } } class TriggerSyncHandler implements OperationStepHandler { @Override public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { final ModelNode syncOperation = new ModelNode(); syncOperation.get(OP).set("calculate-diff-and-sync"); syncOperation.get(OP_ADDR).setEmptyList(); syncOperation.get(DOMAIN_MODEL).set(operation.get(DOMAIN_MODEL)); Resource original = context.readResourceFromRoot(PathAddress.EMPTY_ADDRESS); final HostControllerRegistrationHandler.OperationExecutor internalExecutor = getControllerService().getInternalExecutor(); Map<String, ProxyController> serverProxies = new HashMap<>(); for (Map.Entry<String, MockServerProxy> entry : SyncModelServerStateTestCase.this.serverProxies.entrySet()) { serverProxies.put(entry.getKey(), entry.getValue()); } SyncModelParameters parameters = new SyncModelParameters(new MockDomainController(), ignoredDomainResourceRegistry, hostControllerEnvironment, extensionRegistry, internalExecutor, true, serverProxies, repository, repository); final Resource hostResource = context.readResourceFromRoot(PathAddress.EMPTY_ADDRESS).getChildren(HOST).iterator().next(); final ProductConfig cfg = new ProductConfig("product", "version", "main"); final HostInfo hostInfo = HostInfo.fromModelNode( HostInfo.createLocalHostHostInfo(hostControllerInfo, cfg, ignoredDomainResourceRegistry, hostResource)); final SyncDomainModelOperationHandler handler = new SyncDomainModelOperationHandler(hostInfo, parameters); context.addStep(syncOperation, handler, OperationContext.Stage.MODEL, true); } } private class MockServerProxy implements ProxyController, OperationStepHandler { private final String serverName; private volatile String state = "running"; public MockServerProxy(String serverName) { this.serverName = serverName; } @Override public PathAddress getProxyNodeAddress() { return PathAddress.pathAddress(RUNNING_SERVER, serverName); } @Override public void execute(ModelNode operation, OperationMessageHandler handler, ProxyOperationControl control, OperationAttachments attachments, BlockingTimeout blockingTimeout) { } @Override public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { PathAddress addr = PathAddress.pathAddress(operation.require(OP_ADDR)); Assert.assertEquals(serverName, addr.getLastElement().getValue()); String opName = operation.require(OP).asString(); if (opName.equals(ServerProcessStateHandler.REQUIRE_RESTART_OPERATION)) { state = RESTART_REQUIRED; } else if (opName.equals(ServerProcessStateHandler.REQUIRE_RELOAD_OPERATION)) { state = RELOAD_REQUIRED; } else { throw new IllegalStateException("Unknown state for my intents and purposes"); } } } private static class TestSyncRepository extends TestRepository { //hasContent() gets called as a check on remove so add deployments here to make sure they get removed //from the repository private final Set<String> expectedContent = new HashSet<>(); //Rollout plans need actual content private volatile VirtualFile vf; private final Map<String, ContentReference> addedContentReferences = new HashMap<>(); private final Map<String, ContentReference> fetchedFiles = new HashMap<>(); private final Map<String, ContentReference> removedReferences = new HashMap<>(); @Override public void addContentReference(ContentReference reference) { addedContentReferences.put(reference.getContentIdentifier(),reference); } @Override public File[] getDeploymentFiles(ContentReference reference) { fetchedFiles.put(reference.getContentIdentifier(), reference); return null; } @Override public void removeContent(ContentReference reference) { removedReferences.put(reference.getContentIdentifier(), reference); } @Override public boolean hasContent(byte[] hash) { return expectedContent.contains(HashUtil.bytesToHexString(hash)); } @Override public VirtualFile getContent(byte[] hash) { if (hash[0] == 1) { return vf; } return null; } boolean isEmpty() { return addedContentReferences.isEmpty() && fetchedFiles.isEmpty(); } void checkAddedReferences(byte[] bytes, PathAddress... addresses) { Assert.assertEquals(addresses.length, addedContentReferences.size()); Assert.assertEquals(addresses.length, fetchedFiles.size()); for (PathAddress address : addresses) { ContentReference ref = addedContentReferences.get(address.toCLIStyleString()); Assert.assertNotNull(ref); Assert.assertEquals(HashUtil.bytesToHexString(bytes), ref.getHexHash()); ref = fetchedFiles.get(address.toCLIStyleString()); Assert.assertNotNull(ref); Assert.assertEquals(HashUtil.bytesToHexString(bytes), ref.getHexHash()); } } void checkRemovedReferences(byte[] bytes, PathAddress...addresses) { Assert.assertEquals(addresses.length, removedReferences.size()); for (PathAddress address : addresses) { ContentReference ref = removedReferences.get(address.toCLIStyleString()); Assert.assertNotNull(ref); Assert.assertEquals(HashUtil.bytesToHexString(bytes), ref.getHexHash()); } } void clear(byte[]...currentContent) { expectedContent.clear(); addedContentReferences.clear(); fetchedFiles.clear(); removedReferences.clear(); for (byte[] content : currentContent) { expectedContent.add(HashUtil.bytesToHexString(content)); } } void addRolloutPlan(ModelNode dmr) throws Exception { File systemTmpDir = new File(System.getProperty("java.io.tmpdir")); File tempDir = new File(systemTmpDir, "test" + System.currentTimeMillis()); tempDir.mkdir(); File file = new File(tempDir, "content"); this.vf = VFS.getChild(file.toURI()); try (final PrintWriter out = new PrintWriter(Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8))){ dmr.writeString(out, true); } } void deleteVirtualFileAndParent() { if (vf != null) { vf.delete(); vf.getParent().delete(); } } } }