Support running base testsuite on Windows

Closes #12648

Co-authored-by: Dominik Guhr <dguhr@redhat.com>
This commit is contained in:
Pedro Igor 2022-08-09 04:18:25 -07:00
parent fa383bf76c
commit e3af0610e2
7 changed files with 109 additions and 17 deletions

View file

@ -41,8 +41,8 @@ public final class Environment {
public static final String IMPORT_EXPORT_MODE = "import_export";
public static final String PROFILE ="kc.profile";
public static final String ENV_PROFILE ="KC_PROFILE";
public static final String DATA_PATH = "/data";
public static final String DEFAULT_THEMES_PATH = "/themes";
public static final String DATA_PATH = File.separator + "data";
public static final String DEFAULT_THEMES_PATH = File.separator + "themes";
public static final String DEV_PROFILE_VALUE = "dev";
public static final String PROD_PROFILE_VALUE = "prod";
public static final String LAUNCH_MODE = "kc.launch.mode";

View file

@ -8,6 +8,7 @@ import io.smallrye.config.ConfigSourceInterceptorContext;
import static java.util.Optional.of;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
import java.io.File;
import java.util.Optional;
final class CachingPropertyMappers {
@ -46,7 +47,7 @@ final class CachingPropertyMappers {
if (homeDir == null) {
pathPrefix = "";
} else {
pathPrefix = homeDir + "/conf/";
pathPrefix = homeDir + File.separator + "conf" + File.separator;
}
return of(pathPrefix + value.get());

View file

@ -46,7 +46,7 @@
<shrinkwrap-resolver.version>3.1.4</shrinkwrap-resolver.version>
<selenium.version>3.14.0</selenium.version>
<arquillian-drone.version>2.5.5</arquillian-drone.version>
<arquillian-graphene.version>2.5.4</arquillian-graphene.version>
<arquillian-graphene.version>3.0.0-alpha.3</arquillian-graphene.version>
<arquillian-wildfly-container.version>3.0.1.Final</arquillian-wildfly-container.version>
<arquillian-wls-container.version>1.0.1.Final</arquillian-wls-container.version>
<arquillian-jetty9-container.version>1.0.0.CR3</arquillian-jetty9-container.version>

View file

@ -8,6 +8,11 @@
<arg value="--http-relative-path=/auth"/>
<arg value="--cache=local"/>
</exec>
<exec osfamily="windows" executable="${auth.server.home}/bin/kc.bat" failonerror="true">
<arg value="build"/>
<arg value="--http-relative-path=/auth"/>
<arg value="--cache=local"/>
</exec>
</target>
<macrodef name="bin-chmod">

View file

@ -46,4 +46,4 @@ spi-login-protocol-saml-known-protocols=http=8180,https=8543
# File-Based Vault
vault=file
vault-dir=${kc.home.dir}secrets
vault-dir=${kc.home.dir}/secrets

View file

@ -44,6 +44,10 @@
<mvel.version>2.4.0.Final</mvel.version>
<systemrules.version>1.19.0</systemrules.version>
<common.resources>${basedir}/../../servers/auth-server/jboss/common</common.resources>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<maven.compiler.release>11</maven.compiler.release>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
</properties>
<dependencies>
@ -365,7 +369,10 @@
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
</plugin>
</plugins>
</build>

View file

@ -14,21 +14,26 @@ import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import org.apache.commons.exec.StreamPumper;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.SystemUtils;
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
import org.jboss.arquillian.container.spi.client.container.DeploymentException;
@ -48,6 +53,7 @@ import org.keycloak.testsuite.arquillian.SuiteContext;
*/
public class KeycloakQuarkusServerDeployableContainer implements DeployableContainer<KeycloakQuarkusConfiguration> {
private static final int DEFAULT_SHUTDOWN_TIMEOUT_SECONDS = 10;
private static final String AUTH_SERVER_QUARKUS_MAP_STORAGE_PROFILE = "auth.server.quarkus.mapStorage.profile";
private static final Logger log = Logger.getLogger(KeycloakQuarkusServerDeployableContainer.class);
@ -87,13 +93,17 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
@Override
public void stop() throws LifecycleException {
container.destroy();
if (container.isAlive()) {
try {
destroyDescendantsOnWindows(container, false);
container.destroy();
container.waitFor(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
destroyDescendantsOnWindows(container, true);
container.destroyForcibly();
}
}
}
@Override
public ProtocolDescription getDefaultProtocol() {
@ -118,6 +128,10 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
public void undeploy(Archive<?> archive) throws DeploymentException {
File wrkDir = configuration.getProvidersPath().resolve("providers").toFile();
try {
if (isWindows()) {
// stop before updating providers to avoid file locking issues on Windows
stop();
}
Files.deleteIfExists(wrkDir.toPath().resolve(archive.getName()));
restartServer();
} catch (Exception e) {
@ -172,7 +186,7 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
builder.environment().put("KEYCLOAK_ADMIN_PASSWORD", "admin");
if (restart.compareAndSet(false, true)) {
FileUtils.deleteDirectory(configuration.getProvidersPath().resolve("data").toFile());
deleteDirectory(configuration.getProvidersPath().resolve("data"));
}
return builder.start();
@ -346,7 +360,11 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
additionalBuildArgs = Collections.emptyList();
}
private void deployArchiveToServer(Archive<?> archive) throws IOException {
private void deployArchiveToServer(Archive<?> archive) throws IOException, LifecycleException {
if (isWindows()) {
// stop before updating providers to avoid file locking issues on Windows
stop();
}
File providersDir = configuration.getProvidersPath().resolve("providers").toFile();
InputStream zipStream = archive.as(ZipExporter.class).exportAsInputStream();
Files.copy(zipStream, providersDir.toPath().resolve(archive.getName()), StandardCopyOption.REPLACE_EXISTING);
@ -357,9 +375,9 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
start();
}
private static String getCommand() {
if (SystemUtils.IS_OS_WINDOWS) {
return "kc.bat";
private String getCommand() {
if (isWindows()) {
return configuration.getProvidersPath().resolve("bin").resolve("kc.bat").toString();
}
return "./kc.sh";
}
@ -371,4 +389,65 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta
public void setAdditionalBuildArgs(List<String> newArgs) {
additionalBuildArgs = newArgs;
}
private void destroyDescendantsOnWindows(Process parent, boolean force) {
if (!isWindows()) {
return;
}
CompletableFuture allProcesses = CompletableFuture.completedFuture(null);
for (ProcessHandle process : parent.descendants().collect(Collectors.toList())) {
if (force) {
process.destroyForcibly();
} else {
process.destroy();
}
allProcesses = CompletableFuture.allOf(allProcesses, process.onExit());
}
try {
allProcesses.get(DEFAULT_SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS);
} catch (Exception cause) {
throw new RuntimeException("Failed to terminate descendants processes", cause);
}
try {
// TODO: remove this. do not ask why, but on Windows we are here even though the process was previously terminated
// without this pause, tests re-installing dist before tests should fail
// looks like pausing the current thread let windows to cleanup processes?
// more likely it is env dependent
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
private static boolean isWindows() {
return SystemUtils.IS_OS_WINDOWS;
}
public static void deleteDirectory(final Path directory) throws IOException {
if (Files.isDirectory(directory, new LinkOption[0])) {
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
try {
Files.delete(file);
} catch (IOException var4) {
}
return FileVisitResult.CONTINUE;
}
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
try {
Files.delete(dir);
} catch (IOException var4) {
}
return FileVisitResult.CONTINUE;
}
});
}
}
}