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();
}
}
}
}