package org.keycloak.testsuite.cluster; import org.jboss.arquillian.container.test.api.ContainerController; import org.jboss.arquillian.test.api.ArquillianResource; import org.junit.Before; import org.keycloak.admin.client.Keycloak; import org.keycloak.models.Constants; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.arquillian.ContainerInfo; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import static org.junit.Assert.assertTrue; import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN; import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER; import static org.keycloak.testsuite.util.WaitUtils.pause; /** * * @author tkyjovsk */ public abstract class AbstractClusterTest extends AbstractKeycloakTest { @ArquillianResource protected ContainerController controller; protected Map<ContainerInfo, Keycloak> backendAdminClients = new HashMap<>(); private int currentFailNodeIndex = 0; public int getClusterSize() { return suiteContext.getAuthServerBackendsInfo().size(); } protected void iterateCurrentFailNode() { currentFailNodeIndex++; if (currentFailNodeIndex >= getClusterSize()) { currentFailNodeIndex = 0; } logFailoverSetup(); } // Assume that route like "node6" will have corresponding backend container like "auth-server-wildfly-backend6" protected void setCurrentFailNodeForRoute(String route) { String routeNumber = route.substring(route.length() - 1); currentFailNodeIndex = Integer.parseInt(routeNumber) - 1; } protected ContainerInfo getCurrentFailNode() { return backendNode(currentFailNodeIndex); } protected Set<ContainerInfo> getCurrentSurvivorNodes() { Set<ContainerInfo> survivors = new HashSet<>(suiteContext.getAuthServerBackendsInfo()); survivors.remove(getCurrentFailNode()); return survivors; } protected void logFailoverSetup() { log.info("Current failover setup"); boolean started = controller.isStarted(getCurrentFailNode().getQualifier()); log.info("Fail node: " + getCurrentFailNode() + (started ? "" : " (stopped)")); for (ContainerInfo survivor : getCurrentSurvivorNodes()) { started = controller.isStarted(survivor.getQualifier()); log.info("Survivor: " + survivor + (started ? "" : " (stopped)")); } } public void failure() { log.info("Simulating failure"); killBackendNode(getCurrentFailNode()); } public void failback() { log.info("Bringing all backend nodes online"); for (ContainerInfo node : suiteContext.getAuthServerBackendsInfo()) { startBackendNode(node); } } protected ContainerInfo frontendNode() { return suiteContext.getAuthServerInfo(); } protected ContainerInfo backendNode(int i) { return suiteContext.getAuthServerBackendsInfo().get(i); } protected void startBackendNode(ContainerInfo node) { if (!controller.isStarted(node.getQualifier())) { log.info("Starting backend node: " + node); controller.start(node.getQualifier()); assertTrue(controller.isStarted(node.getQualifier())); } log.info("Backend node " + node + " is started"); if (!backendAdminClients.containsKey(node)) { backendAdminClients.put(node, createAdminClientFor(node)); } } protected Keycloak createAdminClientFor(ContainerInfo node) { log.info("Initializing admin client for " + node.getContextRoot() + "/auth"); return Keycloak.getInstance(node.getContextRoot() + "/auth", MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID); } protected void killBackendNode(ContainerInfo node) { backendAdminClients.get(node).close(); backendAdminClients.remove(node); log.info("Killing backend node: " + node); controller.kill(node.getQualifier()); } protected Keycloak getAdminClientFor(ContainerInfo node) { Keycloak adminClient = backendAdminClients.get(node); if (adminClient == null && node.equals(suiteContext.getAuthServerInfo())) { adminClient = this.adminClient; } return adminClient; } @Before public void beforeClusterTest() { failback(); logFailoverSetup(); pause(3000); } @Override public void addTestRealms(List<RealmRepresentation> testRealms) { // no test realms will be created by the default } }