package org.infinispan.cli.interpreter; import static java.lang.String.format; import static org.infinispan.test.TestingUtil.extractComponent; import static org.infinispan.test.TestingUtil.withCacheManager; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.fail; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; import org.infinispan.Cache; import org.infinispan.cli.interpreter.result.ResultKeys; import org.infinispan.commons.api.BasicCacheContainer; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.context.Flag; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.statetransfer.CommitManager; import org.infinispan.test.CacheManagerCallable; import org.infinispan.xsite.AbstractTwoSitesTest; import org.infinispan.xsite.XSiteAdminOperations; import org.infinispan.xsite.statetransfer.XSiteStateProvider; import org.infinispan.xsite.statetransfer.XSiteStateTransferManager; import org.testng.annotations.Test; /** * @author Tristan Tarrant * @since 5.2 */ @Test(groups = "xsite", testName = "cli.interpreter.SiteStatementTest") public class SiteStatementTest extends AbstractTwoSitesTest { public SiteStatementTest() { implicitBackupCache = true; } @Override protected ConfigurationBuilder getNycActiveConfig() { return getDefaultClusteredCacheConfig(CacheMode.DIST_SYNC, true); } @Override protected ConfigurationBuilder getLonActiveConfig() { return getDefaultClusteredCacheConfig(CacheMode.DIST_SYNC, true); } public void testSiteStatus() throws Exception { Interpreter lonInterpreter = interpreter("LON", 0); String lonCache = cache("LON", 0).getName(); String lonSessionId = lonInterpreter.createSessionId(lonCache); Interpreter nycInterpreter = interpreter("NYC", 0); String nycCache = cache("NYC", 0).getName(); String nycSessionId = nycInterpreter.createSessionId(nycCache); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --status NYC;", "online"); assertInterpreterOutput(nycInterpreter, nycSessionId, format("site --status %s.LON;", lonCache), "online"); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --offline NYC;", "ok"); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --online NYC;", "ok"); } public void testSiteStateTransfer() throws Exception { Interpreter lonInterpreter = interpreter("LON", 0); String lonCache = cache("LON", 0).getName(); String lonSessionId = lonInterpreter.createSessionId(lonCache); Interpreter nycInterpreter = interpreter("NYC", 0); String nycCache = cache("NYC", 0).getName(); String nycSessionId = nycInterpreter.createSessionId(nycCache); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --offline NYC;", "ok"); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --status NYC;", "offline"); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --push NYC;", "ok"); assertEventuallyNoStateTransferInReceivingSite("NYC", nycCache, 10, TimeUnit.SECONDS); assertEventuallyNoStateTransferInSendingSite("LON", lonCache, 10, TimeUnit.SECONDS); assertInterpreterOutput(nycInterpreter, nycSessionId, "site --sendingsite;", "null"); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --pushstatus;", "NYC=OK"); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --clearpushstatus;", "ok"); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --pushstatus;", (String) null); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --cancelpush NYC;", "ok"); assertInterpreterOutput(nycInterpreter, nycSessionId, "site --cancelreceive LON;", "ok"); } public void testSiteWithoutBackups() throws Exception { final String cacheName = "no-backups"; withCacheManager(new CacheManagerCallable(new DefaultCacheManager()) { @Override public void call() { ConfigurationBuilder builder = getDefaultClusteredCacheConfig(CacheMode.LOCAL); builder.sites().disableBackups(true); cm.defineConfiguration(cacheName, builder.build()); Cache cache = cm.getCache(cacheName); Interpreter interpreter = cache.getAdvancedCache().getComponentRegistry().getComponent(Interpreter.class); String sessionId = interpreter.createSessionId(cacheName); try { assertInterpreterError(interpreter, sessionId, "site --status;", "ISPN019033: The cache '" + cacheName + "' has no backups configured."); } catch (Exception e) { throw new RuntimeException(e); } } }); } public void testContainerOperations() throws Exception { site(LON).cacheManagers().forEach(cacheManager -> cacheManager.defineConfiguration("another-cache", lonConfigurationBuilder().build())); site(LON).cacheManagers().forEach(cacheManager -> cacheManager.defineConfiguration("another-cache-2", getDefaultClusteredCacheConfig(CacheMode.DIST_SYNC).build())); site(LON).waitForClusterToForm("another-cache"); site(LON).waitForClusterToForm("another-cache-2"); Interpreter lonInterpreter = interpreter(LON, 0); String lonCache = cache(LON, 0).getName(); String lonSessionId = lonInterpreter.createSessionId(lonCache); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --offlineall NYC;", (output, error) -> { assertEquals(null, error); String outFormat = "%s: %s"; if (!output.contains(format(outFormat, BasicCacheContainer.DEFAULT_CACHE_NAME, XSiteAdminOperations.SUCCESS))) { fail(format("Cache '%s' should be present in the output: %s", BasicCacheContainer.DEFAULT_CACHE_NAME, output)); } if (!output.contains(format(outFormat, "another-cache", XSiteAdminOperations.SUCCESS))) { fail(format("Cache '%s' should be present in the output: %s", "another-cache", output)); } if (output.contains("another-cache-2")) { fail(format("Cache '%s' should not be present in the output: %s", "another-cache-2", output)); } }); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --status NYC;", "offline"); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --status \"another-cache\".NYC;", "offline"); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --onlineall NYC;", (output, error) -> { assertEquals(null, error); String outFormat = "%s: %s"; if (!output.contains(format(outFormat, BasicCacheContainer.DEFAULT_CACHE_NAME, XSiteAdminOperations.SUCCESS))) { fail(format("Cache '%s' should be present in the output: %s", BasicCacheContainer.DEFAULT_CACHE_NAME, output)); } if (!output.contains(format(outFormat, "another-cache", XSiteAdminOperations.SUCCESS))) { fail(format("Cache '%s' should be present in the output: %s", "another-cache", output)); } if (output.contains("another-cache-2")) { fail(format("Cache '%s' should not be present in the output: %s", "another-cache-2", output)); } }); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --status NYC;", "online"); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --status \"another-cache\".NYC;", "online"); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --pushall NYC;", (output, error) -> { assertEquals(null, error); String outFormat = "%s: %s"; if (!output.contains(format(outFormat, BasicCacheContainer.DEFAULT_CACHE_NAME, XSiteAdminOperations.SUCCESS))) { fail(format("Cache '%s' should be present in the output: %s", BasicCacheContainer.DEFAULT_CACHE_NAME, output)); } if (!output.contains(format(outFormat, "another-cache", XSiteAdminOperations.SUCCESS))) { fail(format("Cache '%s' should be present in the output: %s", "another-cache", output)); } if (output.contains("another-cache-2")) { fail(format("Cache '%s' should not be present in the output: %s", "another-cache-2", output)); } }); assertInterpreterOutput(lonInterpreter, lonSessionId, "site --cancelpushall NYC;", (output, error) -> { assertEquals(null, error); String outFormat = "%s: %s"; if (!output.contains(format(outFormat, BasicCacheContainer.DEFAULT_CACHE_NAME, XSiteAdminOperations.SUCCESS))) { fail(format("Cache '%s' should be present in the output: %s", BasicCacheContainer.DEFAULT_CACHE_NAME, output)); } if (!output.contains(format(outFormat, "another-cache", XSiteAdminOperations.SUCCESS))) { fail(format("Cache '%s' should be present in the output: %s", "another-cache", output)); } if (output.contains("another-cache-2")) { fail(format("Cache '%s' should not be present in the output: %s", "another-cache-2", output)); } }); } private void assertInterpreterOutput(Interpreter interpreter, String sessionId, String command, String expected) throws Exception { assertInterpreterOutput(interpreter, sessionId, command, (output, error) -> { assertEquals(null, error); assertEquals(expected, output); }); } private void assertInterpreterOutput(Interpreter interpreter, String sessionId, String command, OutputValidator validator) throws Exception { Objects.requireNonNull(validator); Map<String, String> result = interpreter.execute(sessionId, command); validator.validate(result.get(ResultKeys.OUTPUT.toString()), result.get(ResultKeys.ERROR.toString())); } private void assertInterpreterError(Interpreter interpreter, String sessionId, String command, String expected) throws Exception { assertInterpreterOutput(interpreter, sessionId, command, (output, error) -> { assertEquals(null, output); assertEquals(expected, error); }); } private Interpreter interpreter(String site, int cache) { return cache(site, cache).getAdvancedCache().getComponentRegistry().getComponent(Interpreter.class); } private void assertEventuallyNoStateTransferInReceivingSite(String siteName, String cacheName, long timeout, TimeUnit unit) { assertEventuallyInSite(siteName, cacheName, cache -> { CommitManager commitManager = extractComponent(cache, CommitManager.class); return !commitManager.isTracking(Flag.PUT_FOR_STATE_TRANSFER) && !commitManager.isTracking(Flag.PUT_FOR_X_SITE_STATE_TRANSFER) && commitManager.isEmpty(); }, timeout, unit); } private void assertEventuallyNoStateTransferInSendingSite(String siteName, String cacheName, long timeout, TimeUnit unit) { assertEventuallyInSite(siteName, cacheName, cache -> extractComponent(cache, XSiteStateProvider.class).getCurrentStateSending().isEmpty() && extractComponent(cache, XSiteStateTransferManager.class).getRunningStateTransfers().isEmpty(), timeout, unit); } private interface OutputValidator { void validate(String output, String error); } }