KEYCLOAK-1678 generalized cluster tests for any number of backend nodes

This commit is contained in:
Tomas Kyjovsky 2016-02-15 23:58:50 +01:00
parent 9fd9a1a5ad
commit fa3c0bb0aa
4 changed files with 177 additions and 168 deletions

View file

@ -1,19 +1,20 @@
package org.keycloak.testsuite.cluster;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.jboss.arquillian.container.test.api.ContainerController;
import org.jboss.arquillian.graphene.page.Page;
import org.jboss.arquillian.test.api.ArquillianResource;
import static org.junit.Assert.assertTrue;
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 org.keycloak.testsuite.auth.page.AuthRealm;
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;
/**
*
@ -24,52 +25,91 @@ public abstract class AbstractClusterTest extends AbstractKeycloakTest {
@ArquillianResource
protected ContainerController controller;
protected List<Keycloak> backendAdminClients = new ArrayList<>();
protected Map<ContainerInfo, Keycloak> backendAdminClients = new HashMap<>();
public void startBackendNodes(int count) {
if (count < 0 || count > 10) {
throw new IllegalArgumentException();
private int currentFailNodeIndex = 0;
public int getClusterSize() {
return suiteContext.getAuthServerBackendsInfo().size();
}
protected void iterateCurrentFailNode() {
currentFailNodeIndex++;
if (currentFailNodeIndex >= getClusterSize()) {
currentFailNodeIndex = 0;
}
assertTrue(suiteContext.getAuthServerBackendsInfo().size() >= count);
for (int i = 0; i < count; i++) {
logCurrentState();
}
ContainerInfo backendNode = suiteContext.getAuthServerBackendsInfo().get(i);
protected ContainerInfo getCurrentFailNode() {
return backendNode(currentFailNodeIndex);
}
controller.start(backendNode.getQualifier());
assertTrue(controller.isStarted(backendNode.getQualifier()));
protected Set<ContainerInfo> getCurrentSurvivorNodes() {
Set<ContainerInfo> survivors = new HashSet<>(suiteContext.getAuthServerBackendsInfo());
survivors.remove(getCurrentFailNode());
return survivors;
}
backendAdminClients.add(createAdminClientFor(backendNode));
protected void logCurrentState() {
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)"));
}
}
protected Keycloak createAdminClientFor(ContainerInfo backendNode) {
log.info("Initializing admin client for " + backendNode.getContextRoot() + "/auth");
return Keycloak.getInstance(backendNode.getContextRoot() + "/auth",
MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID);
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 backendNode(int i) {
return suiteContext.getAuthServerBackendsInfo().get(i);
}
protected void startBackendNode(int i) {
String container = backendNode(i).getQualifier();
if (!controller.isStarted(container)) {
controller.start(container);
backendAdminClients.set(i, createAdminClientFor(backendNode(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 void killBackendNode(int i) {
backendAdminClients.get(i).close();
controller.kill(backendNode(i).getQualifier());
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 listRealms(int i) {
log.info(String.format("Node %s: AccessTokenString: %s", i + 1, backendAdminClients.get(i).tokenManager().getAccessTokenString()));
for (RealmRepresentation r : backendAdminClients.get(i).realms().findAll()) {
log.info(String.format("Node %s: Realm: %s, Id: %s", i + 1, r.getRealm(), r.getId()));
}
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 backendNode) {
return backendAdminClients.get(backendNode);
}
@Before
public void beforeClusterTest() {
failback();
logCurrentState();
pause(3000);
}
}

View file

@ -1,57 +0,0 @@
package org.keycloak.testsuite.cluster;
import org.junit.Before;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.testsuite.arquillian.ContainerInfo;
import static org.keycloak.testsuite.util.WaitUtils.pause;
/**
*
* @author tkyjovsk
*/
public abstract class AbstractTwoNodeClusterTest extends AbstractClusterTest {
@Before
public void beforeTwoNodeClusterTest() {
startBackendNodes(2);
pause(3000);
}
protected ContainerInfo backend1Info() {
return backendNode(0);
}
protected ContainerInfo backend2Info() {
return backendNode(1);
}
protected Keycloak backend1AdminClient() {
return backendAdminClients.get(0);
}
protected Keycloak backend2AdminClient() {
return backendAdminClients.get(1);
}
protected void startBackend1() {
startBackendNode(0);
}
protected void startBackend2() {
startBackendNode(1);
}
protected void failback() {
startBackend1();
startBackend2();
}
protected void killBackend1() {
killBackendNode(0);
}
protected void killBackend2() {
killBackendNode(1);
}
}

View file

@ -5,81 +5,19 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import org.junit.Test;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.arquillian.ContainerInfo;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
/**
*
* @author tkyjovsk
*/
public class EntityInvalidationClusterTest extends AbstractTwoNodeClusterTest {
public class EntityInvalidationClusterTest extends AbstractClusterTest {
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
}
@Test
public void realmCRUDWithoutFailover() {
realmCRUD(TEST + "_wofo", false);
}
@Test
public void realmCRUDWithFailover() {
realmCRUD(TEST + "_wfo", true);
}
public void realmCRUD(String realm, boolean containerFailover) {
RealmRepresentation testRealm = new RealmRepresentation();
testRealm.setRealm(realm);
testRealm.setEnabled(true);
// CREATE on node1
backend1AdminClient().realms().create(testRealm);
// check if created on node1
RealmRepresentation testRealmOnBackend1 = backend1AdminClient().realms().realm(realm).toRepresentation();
assertEquals(testRealmOnBackend1.getRealm(), testRealm.getRealm());
if (containerFailover) {
killBackend1();
}
// check if created on node2
RealmRepresentation testRealmOnBackend2 = backend2AdminClient().realms().realm(realm).toRepresentation();
assertEquals(testRealmOnBackend1.getId(), testRealmOnBackend2.getId());
assertEquals(testRealmOnBackend1.getRealm(), testRealmOnBackend2.getRealm());
failback();
// UPDATE on node2
String realmUpdated = realm + "_updated";
testRealmOnBackend2.setRealm(realmUpdated);
backend2AdminClient().realms().realm(realm).update(testRealmOnBackend2);
if (containerFailover) {
killBackend2();
}
// check if updated on node1
testRealmOnBackend1 = backend1AdminClient().realms().realm(realmUpdated).toRepresentation();
assertEquals(testRealmOnBackend1.getId(), testRealmOnBackend2.getId());
assertEquals(testRealmOnBackend1.getRealm(), testRealmOnBackend2.getRealm());
failback();
// DELETE on node1
backend1AdminClient().realms().realm(realmUpdated).remove();
if (containerFailover) {
killBackend1();
}
// check if deleted on node2
boolean testRealmOnBackend2Exists = false;
for (RealmRepresentation realmOnBackend2 : backend2AdminClient().realms().findAll()) {
if (realmUpdated.equals(realmOnBackend2.getRealm())
|| testRealmOnBackend1.getId().equals(realmOnBackend2.getId())) {
testRealmOnBackend2Exists = true;
break;
}
}
assertFalse(testRealmOnBackend2Exists);
}
@Test
public void createRealmViaFrontend() {
String realm = TEST + "_fe";
@ -95,15 +33,102 @@ public class EntityInvalidationClusterTest extends AbstractTwoNodeClusterTest {
RealmRepresentation testRealmOnFrontend = adminClient.realms().realm(realm).toRepresentation();
assertEquals(testRealmOnFrontend.getRealm(), testRealm.getRealm());
// check if created on node1
RealmRepresentation testRealmOnBackend1 = backend1AdminClient().realms().realm(realm).toRepresentation();
assertEquals(testRealmOnBackend1.getId(), testRealmOnFrontend.getId());
assertEquals(testRealmOnBackend1.getRealm(), testRealmOnFrontend.getRealm());
// check if created on node2
RealmRepresentation testRealmOnBackend2 = backend2AdminClient().realms().realm(realm).toRepresentation();
assertEquals(testRealmOnBackend2.getId(), testRealmOnFrontend.getId());
assertEquals(testRealmOnBackend2.getRealm(), testRealmOnFrontend.getRealm());
// check if created on backend nodes
for (ContainerInfo backend : suiteContext.getAuthServerBackendsInfo()) {
RealmRepresentation testRealmOnBackend = getAdminClientFor(backend).realms().realm(realm).toRepresentation();
assertEquals(testRealmOnBackend.getId(), testRealmOnFrontend.getId());
assertEquals(testRealmOnBackend.getRealm(), testRealmOnFrontend.getRealm());
}
}
@Test
public void realmCRUDWithoutFailover() {
realmCRUD(TEST + "_wofo", false);
}
@Test
public void realmCRUDWithFailover() {
realmCRUD(TEST + "_wfo", true);
}
public void realmCRUD(String realmName, boolean backendFailover) {
// CREATE on current fail node
log.info("Creating realm on : " + getCurrentFailNode());
RealmRepresentation testRealm = new RealmRepresentation();
testRealm.setRealm(realmName);
testRealm.setEnabled(true);
getAdminClientFor(getCurrentFailNode()).realms().create(testRealm);
// check if created on fail node
RealmRepresentation testRealmOnFailNode = getAdminClientFor(getCurrentFailNode()).realms().realm(realmName).toRepresentation();
assertEquals(testRealmOnFailNode.getRealm(), testRealm.getRealm());
if (backendFailover) {
failure();
}
// check if created on survivor nodes
for (ContainerInfo survivorNode : getCurrentSurvivorNodes()) {
RealmRepresentation testRealmOnSurvivorNode = getAdminClientFor(survivorNode).realms().realm(realmName).toRepresentation();
assertEquals(testRealmOnFailNode.getId(), testRealmOnSurvivorNode.getId());
assertEquals(testRealmOnFailNode.getRealm(), testRealmOnSurvivorNode.getRealm());
log.info("Creation on survivor: " + survivorNode + " verified");
}
failback();
iterateCurrentFailNode();
// UPDATE on current fail node
log.info("Updating realm on: " + getCurrentFailNode());
String realmBeforeUpdate = realmName;
realmName += "_updated";
testRealm = testRealmOnFailNode;
testRealm.setRealm(realmName);
getAdminClientFor(getCurrentFailNode()).realms().realm(realmBeforeUpdate).update(testRealm);
// check if updated on fail node
testRealmOnFailNode = getAdminClientFor(getCurrentFailNode()).realms().realm(realmName).toRepresentation();
assertEquals(testRealmOnFailNode.getRealm(), testRealm.getRealm());
if (backendFailover) {
failure();
}
// check if updated on survivor nodes
for (ContainerInfo survivorNode : getCurrentSurvivorNodes()) {
RealmRepresentation testRealmOnSurvivorNode = getAdminClientFor(survivorNode).realms().realm(realmName).toRepresentation();
assertEquals(testRealmOnFailNode.getId(), testRealmOnSurvivorNode.getId());
assertEquals(testRealmOnFailNode.getRealm(), testRealmOnSurvivorNode.getRealm());
log.info("Update on survivor: " + survivorNode + " verified");
}
failback();
iterateCurrentFailNode();
// DELETE on current fail node
log.info("Deleting realm on: " + getCurrentFailNode());
getAdminClientFor(getCurrentFailNode()).realms().realm(realmName).remove();
if (backendFailover) {
failure();
}
// check if deleted from all survivor nodes
boolean realmStillExists = false;
for (ContainerInfo survivorNode : getCurrentSurvivorNodes()) {
boolean realmStillExistsOnSurvivor = false;
for (RealmRepresentation realmOnSurvivorNode : getAdminClientFor(survivorNode).realms().findAll()) {
if (realmName.equals(realmOnSurvivorNode.getRealm())
|| testRealmOnFailNode.getId().equals(realmOnSurvivorNode.getId())) {
realmStillExistsOnSurvivor = true;
realmStillExists = true;
break;
}
}
log.error("Deletion on survivor: " + survivorNode + (realmStillExistsOnSurvivor ? " FAILED" : " verified"));
}
assertFalse(realmStillExists);
}
}

View file

@ -17,7 +17,7 @@ import org.openqa.selenium.Cookie;
*
* @author tkyjovsk
*/
public class SessionFailoverClusterTest extends AbstractTwoNodeClusterTest {
public class SessionFailoverClusterTest extends AbstractClusterTest {
public static final String KEYCLOAK_SESSION_COOKIE = "KEYCLOAK_SESSION";
public static final String KEYCLOAK_IDENTITY_COOKIE = "KEYCLOAK_IDENTITY";
@ -30,7 +30,7 @@ public class SessionFailoverClusterTest extends AbstractTwoNodeClusterTest {
@Ignore("work in progress") // only works with owners="2" at the moment
public void sessionFailover() {
// LOGOUT
// LOGIN
accountPage.navigateTo();
driver.navigate().refresh();
pause(3000);
@ -40,7 +40,7 @@ public class SessionFailoverClusterTest extends AbstractTwoNodeClusterTest {
Cookie sessionCookie = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
assertNotNull(sessionCookie);
killBackend1();
failure();
// check if session survived backend failure
@ -53,6 +53,7 @@ public class SessionFailoverClusterTest extends AbstractTwoNodeClusterTest {
assertEquals(sessionCookieAfterFailover.getValue(), sessionCookie.getValue());
failback();
iterateCurrentFailNode();
// check if session survived backend failback
driver.navigate().refresh();
@ -71,7 +72,7 @@ public class SessionFailoverClusterTest extends AbstractTwoNodeClusterTest {
sessionCookie = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
assertNull(sessionCookie);
killBackend1();
failure();
// check if session survived backend failure
driver.navigate().refresh();