diff --git a/.gitignore b/.gitignore index da1dad7b35..b5e00374d8 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ nbproject # Logs and databases # ###################### *.log +.attach_pid* # Maven # ######### diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/legacy/assembly.xml b/testsuite/integration-arquillian/servers/auth-server/jboss/legacy/assembly.xml new file mode 100644 index 0000000000..b919fa1c95 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/jboss/legacy/assembly.xml @@ -0,0 +1,46 @@ + + + + + auth-server-legacy + + + zip + + + false + + + + ${auth.server.home} + auth-server-legacy + + **/*.sh + + + + ${auth.server.home} + auth-server-legacy + + **/*.sh + + 0755 + + + + diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/legacy/pom.xml b/testsuite/integration-arquillian/servers/auth-server/jboss/legacy/pom.xml new file mode 100644 index 0000000000..45dfdec434 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/jboss/legacy/pom.xml @@ -0,0 +1,82 @@ + + + + + + org.keycloak.testsuite + integration-arquillian-servers-auth-server-jboss + 11.0.0-SNAPSHOT + + 4.0.0 + + pom + + integration-arquillian-servers-auth-server-legacy + + Auth Server - Legacy + + + ${auth.server.legacy.version} + keycloak-${auth.server.legacy.version} + + + + + + maven-enforcer-plugin + + false + + + + org.apache.maven.plugins + maven-assembly-plugin + + + create-zip + package + + single + + + + assembly.xml + + false + + + + + + + + + + product + + + product + + + + ${product.name}-${auth.server.legacy.filename.version} + + + + diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/legacy/src/.dont-delete b/testsuite/integration-arquillian/servers/auth-server/jboss/legacy/src/.dont-delete new file mode 100644 index 0000000000..63f93b0dd0 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/jboss/legacy/src/.dont-delete @@ -0,0 +1 @@ +This file is to mark this Maven project as a valid option for building auth server artifact diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml index e72989c356..d2b263c8f5 100644 --- a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml @@ -655,6 +655,18 @@ + + auth-server-legacy + + + auth.server.legacy.version + + + + legacy + + + auth-server-wildfly diff --git a/testsuite/integration-arquillian/tests/base/.attach_pid34555 b/testsuite/integration-arquillian/tests/base/.attach_pid34555 deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index e85aa1be00..eec96ddec0 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -126,6 +126,11 @@ org.apache.maven.resolver maven-resolver-api + + org.jboss + jandex + 2.1.3.Final + diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java index 11b09c933c..44aebaa881 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java @@ -121,6 +121,8 @@ public class AuthServerTestEnricher { public static final String AUTH_SERVER_BACKEND_PROPERTY = "auth.server.backend"; public static final String AUTH_SERVER_BACKEND = System.getProperty(AUTH_SERVER_BACKEND_PROPERTY, AUTH_SERVER_BACKEND_DEFAULT); + public static final String AUTH_SERVER_LEGACY = "auth-server-legacy"; + public static final String AUTH_SERVER_BALANCER_DEFAULT = "auth-server-balancer"; public static final String AUTH_SERVER_BALANCER_PROPERTY = "auth.server.balancer"; public static final String AUTH_SERVER_BALANCER = System.getProperty(AUTH_SERVER_BALANCER_PROPERTY, AUTH_SERVER_BALANCER_DEFAULT); @@ -298,6 +300,15 @@ public class AuthServerTestEnricher { suiteContext.addAuthServerBackendsInfo(0, c); }); + if (Boolean.parseBoolean(System.getProperty("auth.server.jboss.legacy"))) { + ContainerInfo legacy = containers.stream() + .filter(c -> c.getQualifier().startsWith(AUTH_SERVER_LEGACY)) + .findAny() + .orElseThrow(() -> new IllegalStateException("Not found legacy container: " + AUTH_SERVER_LEGACY)); + updateWithAuthServerInfo(legacy, 500); + suiteContext.setLegacyAuthServerInfo(legacy); + } + if (suiteContext.getAuthServerBackendsInfo().isEmpty()) { throw new RuntimeException(String.format("No auth server container matching '%s' found in arquillian.xml.", AUTH_SERVER_BACKEND)); } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java index 5be255f023..9129edc2a0 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java @@ -40,6 +40,7 @@ public final class SuiteContext { private List authServerInfo = new LinkedList<>(); private final List> authServerBackendsInfo = new ArrayList<>(); + private ContainerInfo legacyAuthServerInfo; private final List cacheServersInfo = new ArrayList<>(); @@ -149,6 +150,14 @@ public final class SuiteContext { authServerBackendsInfo.get(dcIndex).add(container); } + public ContainerInfo getLegacyAuthServerInfo() { + return legacyAuthServerInfo; + } + + public void setLegacyAuthServerInfo(ContainerInfo legacyAuthServerInfo) { + this.legacyAuthServerInfo = legacyAuthServerInfo; + } + public ContainerInfo getMigratedAuthServerInfo() { return migratedAuthServerInfo; } @@ -205,6 +214,9 @@ public final class SuiteContext { .append("\n"); getAuthServerBackendsInfo().forEach(bInfo -> sb.append(" Backend: ").append(bInfo).append(" - ").append(bInfo.getContextRoot().toExternalForm()).append("\n")); + if (Boolean.parseBoolean(System.getProperty("auth.server.jboss.legacy"))) { + sb.append(" Legacy: ").append(getLegacyAuthServerInfo()).append(" - ").append(getLegacyAuthServerInfo().getContextRoot().toExternalForm()).append("\n"); + } } else { sb.append(getAuthServerInfo().getQualifier()) .append("\n"); diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/TestClassProvider.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/TestClassProvider.java index ce609fa011..1a8877eb74 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/TestClassProvider.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/TestClassProvider.java @@ -23,6 +23,7 @@ public class TestClassProvider { "/org/jboss/resteasy/client", "/org/jboss/arquillian", "/org/jboss/shrinkwrap", + "/org/jboss/jandex", "/org/openqa/selenium" }; diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakContainerEventsController.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakContainerEventsController.java index d2459a0a33..59c7318831 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakContainerEventsController.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakContainerEventsController.java @@ -40,7 +40,6 @@ import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.exporter.ZipExporter; import org.jboss.shrinkwrap.api.spec.WebArchive; -import org.keycloak.common.Profile; import org.keycloak.helpers.DropAllServlet; import org.keycloak.testsuite.arquillian.ContainerInfo; import org.keycloak.testsuite.arquillian.annotation.RestartContainer; @@ -53,14 +52,7 @@ import org.wildfly.extras.creaper.core.online.OnlineOptions; import java.io.File; import java.io.IOException; -import java.io.PrintWriter; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Optional; -import java.util.Properties; +import org.jboss.shrinkwrap.api.Archive; import org.keycloak.testsuite.util.ContainerAssume; /** @@ -251,4 +243,23 @@ public class KeycloakContainerEventsController extends ContainerEventController } } } + + public static void deploy(Archive archive, ContainerInfo containerInfo) throws CommandFailedException, IOException { + ManagementClient.online(OnlineOptions + .standalone() + .hostAndPort("localhost", containerInfo.getContextRoot().getPort() + 1547) + .build()) + .apply(new Deploy.Builder( + archive.as(ZipExporter.class).exportAsInputStream(), + archive.getName(), + true).build()); + } + + public static void undeploy(Archive archive, ContainerInfo containerInfo) throws CommandFailedException, IOException { + ManagementClient.online(OnlineOptions + .standalone() + .hostAndPort("localhost", containerInfo.getContextRoot().getPort() + 1547) + .build()) + .apply(new Undeploy.Builder(archive.getName()).build()); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/cluster/MultiVersionClusterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/cluster/MultiVersionClusterTest.java new file mode 100644 index 0000000000..163684aaff --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/cluster/MultiVersionClusterTest.java @@ -0,0 +1,388 @@ +/* + * Copyright 2020 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.migration.cluster; + +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.stream.Collectors; +import org.apache.commons.io.FileUtils; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.notNullValue; +import org.infinispan.Cache; +import org.infinispan.remoting.RemoteException; +import org.jboss.arquillian.graphene.page.Page; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.DotName; +import org.jboss.jandex.Indexer; +import org.jboss.modules.Module; +import org.jboss.modules.ModuleClassLoader; +import org.jboss.modules.ModuleLoader; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.After; +import org.junit.Assert; +import static org.junit.Assert.assertThat; +import org.junit.Assume; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.keycloak.OAuth2Constants; +import org.keycloak.cluster.ClusterEvent; +import org.keycloak.cluster.infinispan.WrapperClusterEvent; +import org.keycloak.common.util.reflections.Reflections; +import org.keycloak.connections.infinispan.InfinispanConnectionProvider; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.UserSessionModel; +import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionStore; +import org.keycloak.models.sessions.infinispan.entities.SessionEntity; +import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testsuite.arquillian.ContainerInfo; +import org.keycloak.testsuite.cluster.AbstractClusterTest; +import static org.keycloak.testsuite.arquillian.containers.KeycloakContainerEventsController.deploy; +import static org.keycloak.testsuite.arquillian.containers.KeycloakContainerEventsController.undeploy; +import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER; +import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.rest.TestClassLoader; +import org.keycloak.testsuite.runonserver.RunOnServerException; +import org.keycloak.testsuite.runonserver.SerializationUtil; +import org.keycloak.testsuite.util.DroneUtils; +import org.keycloak.testsuite.util.OAuthClient; + +public class MultiVersionClusterTest extends AbstractClusterTest { + + private static ContainerInfo currentNode; + private static ContainerInfo legacyNode; + private static boolean initialized = false; + + @Page + protected LoginPage loginPage; + + static class CacheValuesHolder { + private Map> values; + + public CacheValuesHolder() { + } + + public CacheValuesHolder(final Map> values) { + this.values = values; + } + + public Map> getValues() { + return values; + } + + public void setValues(Map> values) { + this.values = values; + } + } + + @BeforeClass + public static void enabled() { + Assume.assumeThat(System.getProperty("auth.server.legacy.version"), notNullValue()); + } + + @Before + @Override + public void beforeClusterTest() { + if (!initialized) { + currentNode = backendNode(0); + legacyNode = suiteContext.getLegacyAuthServerInfo(); + addAdminJsonFileToLegacy(); + + initialized = true; + } + startBackendNode(legacyNode); + startBackendNode(currentNode); + } + + @After + public void after() { + killBackendNode(legacyNode); + killBackendNode(currentNode); + } + + private JavaArchive deployment() { + return ShrinkWrap.create(JavaArchive.class, "negative.jar") + .addPackage("org/keycloak/testsuite") + .addClass(SerializableTestClass.class); + } + + @Test + public void verifyFailureOnLegacy() throws Exception { + + deploy(deployment(), currentNode); + + try { + backendTestingClients.get(currentNode).server().run(session -> { + try { + Class itShouldFail = Module.getContextModuleLoader().loadModule("deployment.negative.jar").getClassLoader() + .loadClassLocal(SerializableTestClass.class.getName()); + session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) + .put("itShouldFail", Reflections.newInstance(itShouldFail)); + } catch (Exception ex) { + throw new RunOnServerException(ex); + } + }); + } catch (Exception e) { + assertThat(e, instanceOf(RunOnServerException.class)); + assertThat(e.getCause().getCause(), instanceOf(RemoteException.class)); + } finally { + undeploy(deployment(), currentNode); + } + } + + @Test + public void verifyFailureOnCurrent() throws Exception { + + deploy(deployment(), legacyNode); + + try { + backendTestingClients.get(legacyNode).server().run(session -> { + try { + Class itShouldFail = Module.getContextModuleLoader().loadModule("deployment.negative.jar").getClassLoader() + .loadClassLocal(SerializableTestClass.class.getName()); + session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME) + .put("itShouldFail", Reflections.newInstance(itShouldFail)); + } catch (Exception ex) { + throw new RunOnServerException(ex); + } + }); + } catch (Exception e) { + assertThat(e, instanceOf(RunOnServerException.class)); + assertThat(e.getCause().getCause(), instanceOf(RemoteException.class)); + } finally { + undeploy(deployment(), legacyNode); + } + } + + /* + * Tests if legacy node remains usable (login) after current node connects to cluster + */ + @Test + public void loginSuccessToLegacy() throws Exception { + String originalServerRoot = OAuthClient.SERVER_ROOT; + try { + OAuthClient.updateURLs(legacyNode.getContextRoot().toString()); + OAuthClient oauth = new OAuthClient(); + oauth.init(DroneUtils.getCurrentDriver()); + oauth.realm(MASTER).clientId("account").redirectUri(legacyNode.getContextRoot().toString() + "/auth/realms/master/account/"); + + oauth.openLoginForm(); + assertThat(DroneUtils.getCurrentDriver().getTitle(), containsString("Log in to ")); + loginPage.login("admin", "admin"); + + assertThat("Login was not successful.", oauth.getCurrentQuery().get(OAuth2Constants.CODE), notNullValue()); + } finally { + OAuthClient.updateURLs(originalServerRoot); + } + } + + @Test + public void fromLegacyToCurrent() { + Map> expected = createCacheAndGetFromServer(legacyNode); + Map> actual = getFromServer(currentNode, SerializationUtil.encode(expected.keySet().toString())); + Assert.assertThat(actual, equalTo(expected)); + } + + @Test + public void fromCurrentToLegacy() { + Map> expected = createCacheAndGetFromServer(currentNode); + Map> actual = getFromServer(legacyNode, SerializationUtil.encode(expected.keySet().toString())); + Assert.assertThat(actual, equalTo(expected)); + } + + private void addAdminJsonFileToLegacy() { + try { + FileUtils.copyFile(new File("target/test-classes/keycloak-add-user.json"), + new File(System.getProperty("auth.server.legacy.home") + + "/standalone/configuration/keycloak-add-user.json")); + log.debug("Successfully added keycloak-add-user.json to " + System.getProperty("auth.server.legacy.home") + + "/standalone/configuration/keycloak-add-user.json"); + } catch (IOException ex) { + throw new RuntimeException("Adding admin json file failed.", ex); + } + } + + private Map> createCacheAndGetFromServer(ContainerInfo container) { + return backendTestingClients.get(container).server().fetch(session -> { + Map> result = new HashMap<>(); + + try { + Indexer indexer = new Indexer(); + DotName serializeWith = DotName.createSimple("org.infinispan.commons.marshall.SerializeWith"); + + ModuleLoader contextModuleLoader = Module.getContextModuleLoader(); + Module module = contextModuleLoader.loadModule("org.keycloak.keycloak-model-infinispan"); + ModuleClassLoader classLoader = module.getClassLoader(); + + Enumeration resources = classLoader.getResources("org/keycloak"); + while (resources.hasMoreElements()) { + URL nextElement = resources.nextElement(); + Enumeration entries = new JarFile(nextElement.getFile().replace("file:", "").replace("!/org/keycloak", "")).entries(); + + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (entry.getName().endsWith(".class")) { + indexer.index(classLoader.getResourceAsStream(entry.getName())); + } + } + } + + Cache cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME); + + for (AnnotationInstance annotation : indexer.complete().getAnnotations(serializeWith)) { + + switch (annotation.target().kind()) { + case CLASS: + Map fieldValue = new HashMap<>(); + String className = annotation.target().asClass().name().toString(); + Class classForName = Reflections.classForName(className, classLoader); + Object newInstance; + + if (Arrays.asList(classForName.getDeclaredConstructors()).stream() + .filter(c -> !c.isSynthetic()) + .anyMatch(c -> c.getParameterTypes().length == 0 )) { + newInstance = Reflections.newInstance(classForName); + } else { + Constructor constructor = Arrays.asList(classForName.getDeclaredConstructors()).stream() + .filter(c -> !c.isSynthetic()) + .findFirst().get(); + constructor.setAccessible(true); + + List parameters = new ArrayList<>(); + for (Class type : constructor.getParameterTypes()) { + if (type.isPrimitive()) { // we have to set all primitive values in constructor + if (type.equals(Boolean.TYPE)) { + parameters.add(false); + } else if (type.equals(Character.TYPE)) { + parameters.add(' '); + } else { + parameters.add(0); + } + } else if (type.equals(UUID.class)) { //UUID cannot be null + parameters.add(UUID.randomUUID()); + } else { + parameters.add(null); // all fields will be set in next step + } + } + newInstance = constructor.newInstance(parameters.toArray()); + } + + Set fields = Reflections.getAllDeclaredFields(classForName).stream() + .filter(field -> !Modifier.isStatic(field.getModifiers())) + .collect(Collectors.toSet()); + + for (Field field : fields) { + field.setAccessible(true); + Class type = field.getType(); + Object value; + if (type.equals(KeycloakSession.class)) { + value = session; + } else if (type.equals(String.class)) { + value = UUID.randomUUID().toString(); + } else if (type.equals(Boolean.TYPE) || type.equals(Boolean.class)) { + value = Boolean.FALSE; + } else if (type.equals(Integer.TYPE) || type.equals(Integer.class)) { + value = new Random().nextInt(); + } else if (type.equals(Long.TYPE) || type.equals(Long.class)) { + value = new Random().nextLong(); + } else if (type.equals(AuthenticatedClientSessionStore.class)) { + value = new AuthenticatedClientSessionStore(); + } else if (type.equals(UserSessionModel.State.class)) { + value = UserSessionModel.State.LOGGING_OUT; + } else if (type.equals(Map.class)) { + value = new HashMap(); + } else if (type.equals(ConcurrentHashMap.class)) { + value = new ConcurrentHashMap(); + } else if (type.equals(Set.class)) { + value = new HashSet(); + } else if (type.equals(ClusterEvent.class)) { + value = new WrapperClusterEvent(); + } else if (type.equals(UUID.class)) { + value = UUID.randomUUID(); + } else if (type.equals(SessionEntity.class)) { + value = new UserSessionEntity(); + } else if (type.equals(BitSet.class)) { + value = new BitSet(); + } else { + throw new IllegalStateException(className + " - Uncovered parameter type: " + type); + } + field.set(newInstance, value); + fieldValue.put(field.getName(), value); + } + + cache.put(className, newInstance); + result.put(className, fieldValue); + break; + } + } + } catch (Exception ex) { + throw new RuntimeException(ex); + } + return new CacheValuesHolder(result); + }, CacheValuesHolder.class).getValues(); + } + + private Map> getFromServer(ContainerInfo container, final String classes) { + return backendTestingClients.get(container).server().fetch(session -> { + + Map> mapa = new HashMap<>(); + Cache cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME); + + String decoded = (String) SerializationUtil.decode(classes, TestClassLoader.getInstance()); + for (String className : decoded.replace("[", "").replace("]", "").split(", ")) { + Map fieldValues = new HashMap<>(); + Object cacheEntry = cache.get(className); + Reflections.getAllDeclaredFields(cacheEntry.getClass()).stream() + .filter(field -> !Modifier.isStatic(field.getModifiers())) + .forEach(field -> { + field.setAccessible(true); + Object fieldValue = Reflections.getFieldValue(field, cacheEntry); + fieldValues.put(field.getName(), fieldValue); + }); + mapa.put(className, fieldValues); + } + return new CacheValuesHolder(mapa); + }, CacheValuesHolder.class).getValues(); + } + + @Override + public void addTestRealms(List testRealms) { + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/cluster/SerializableTestClass.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/cluster/SerializableTestClass.java new file mode 100644 index 0000000000..79010b9b8c --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/cluster/SerializableTestClass.java @@ -0,0 +1,47 @@ +/* + * Copyright 2020 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.migration.cluster; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.Serializable; +import org.infinispan.commons.marshall.Externalizer; +import org.infinispan.commons.marshall.SerializeWith; + +@SerializeWith(SerializableTestClass.ExternalizerImpl.class) +public class SerializableTestClass implements Serializable { + public static class ExternalizerImpl implements Externalizer { + + private static final int VERSION_1 = 1; + + @Override + public void writeObject(ObjectOutput oo, SerializableTestClass t) throws IOException { + oo.writeByte(VERSION_1); + } + + @Override + public SerializableTestClass readObject(ObjectInput oi) throws IOException, ClassNotFoundException { + switch (oi.readByte()) { + case VERSION_1: + return new SerializableTestClass(); + default: + throw new IOException("Unknown version"); + } + } + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml index c3a6c24403..45e727a24c 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml @@ -174,6 +174,7 @@ -Dauth.server.db.host=${auth.server.db.host} + ${auth.server.backend1.jvm.debug.args} ${auth.server.memory.settings} -Djava.net.preferIPv4Stack=true ${auth.server.jvm.args.extra} @@ -201,6 +202,7 @@ -Dauth.server.db.host=${auth.server.db.host} + ${auth.server.backend2.jvm.debug.args} ${auth.server.memory.settings} -Djava.net.preferIPv4Stack=true ${auth.server.jvm.args.extra} @@ -211,6 +213,32 @@ ${auth.server.backend2.port.offset} + + + ${auth.server.jboss.legacy} + org.jboss.as.arquillian.container.managed.ManagedDeployableContainer + ${auth.server.legacy.home} + standalone-ha.xml + + -Djboss.as.management.blocking.timeout=${auth.server.jboss.startup.timeout} + -Djboss.socket.binding.port-offset=${auth.server.legacy.port.offset} + -Djboss.node.name=legacy + -Dauth.server.truststore=${auth.server.truststore} + -Dauth.server.truststore.password=${auth.server.truststore.password} + -Dauth.server.db.host=${auth.server.db.host} + + + ${auth.server.legacy.jvm.debug.args} + ${auth.server.memory.settings} + -Djava.net.preferIPv4Stack=true + ${auth.server.jvm.args.extra} + + true + ${auth.server.legacy.management.port} + ${auth.server.jboss.startup.timeout} + ${auth.server.legacy.port.offset} + + diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml index 6157facc68..5ba442a82c 100755 --- a/testsuite/integration-arquillian/tests/pom.xml +++ b/testsuite/integration-arquillian/tests/pom.xml @@ -45,6 +45,7 @@ false false false + false false false @@ -578,6 +579,7 @@ ${auth.server.cluster} ${auth.server.undertow.cluster} ${auth.server.jboss.cluster} + ${auth.server.jboss.legacy} ${auth.server.crossdc} @@ -1192,6 +1194,11 @@ ${containers.home}/auth-server-${auth.server}-backend1 ${containers.home}/auth-server-${auth.server}-backend2 + 5008 + 5009 + + ${containers.home}/auth-server-legacy + 5010 ${auth.server.backend1.home}/standalone/configuration @@ -1256,6 +1263,8 @@ ${auth.server.backend1.home} ${auth.server.backend2.home} + -agentlib:jdwp=transport=dt_socket,server=y,suspend=${auth.server.debug.suspend},address=${auth.server.host}:${auth.server.backend1.debug.port} + -agentlib:jdwp=transport=dt_socket,server=y,suspend=${auth.server.debug.suspend},address=${auth.server.host}:${auth.server.backend2.debug.port} 101 @@ -1272,6 +1281,11 @@ 10100 10101 + + ${auth.server.legacy.home} + -agentlib:jdwp=transport=dt_socket,server=y,suspend=${auth.server.debug.suspend},address=${auth.server.host}:${auth.server.legacy.debug.port} + 600 + 10590 @@ -1280,6 +1294,47 @@ + + auth-server-legacy + + + auth.server.legacy.version + + + + true + + + + + + maven-dependency-plugin + + + unpack-auth-server-legacy + generate-resources + + unpack + + + + + org.keycloak.testsuite + integration-arquillian-servers-auth-server-legacy + ${project.version} + zip + + + ${containers.home} + true + + + + + + + + clean-jpa