keycloak-uncached

KEYCLOAK-7101 Fix DockerClientTest

4/26/2018 2:00:49 PM

Changes

testsuite/integration-arquillian/tests/base/src/test/resources/dockerClientTest/keycloak-docker-compose-yaml/certs/docker-realm-public-key.pem 17(+0 -17)

testsuite/integration-arquillian/tests/base/src/test/resources/dockerClientTest/keycloak-docker-compose-yaml/certs/localhost.crt 35(+0 -35)

testsuite/integration-arquillian/tests/base/src/test/resources/dockerClientTest/keycloak-docker-compose-yaml/certs/localhost.key 51(+0 -51)

testsuite/integration-arquillian/tests/base/src/test/resources/dockerClientTest/keycloak-docker-compose-yaml/sysconfig_docker 45(+0 -45)

testsuite/integration-arquillian/tests/base/src/test/resources/docker-test-realm.json 1315(+0 -1315)

Details

diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml
index 674a92f..9c8854c 100644
--- a/testsuite/integration-arquillian/pom.xml
+++ b/testsuite/integration-arquillian/pom.xml
@@ -52,6 +52,7 @@
         <version.shrinkwrap.resolvers>2.2.6</version.shrinkwrap.resolvers>
         <undertow-embedded.version>1.0.0.Alpha2</undertow-embedded.version>
         <version.org.wildfly.extras.creaper>1.6.1</version.org.wildfly.extras.creaper>
+        <testcontainers.version>1.5.1</testcontainers.version>
 
         <!--migration properties-->
         <migration.70.version>1.9.8.Final</migration.70.version>
diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml
index 7bffe00..a131f6a 100644
--- a/testsuite/integration-arquillian/tests/base/pom.xml
+++ b/testsuite/integration-arquillian/tests/base/pom.xml
@@ -112,7 +112,7 @@
         <dependency>
             <groupId>org.testcontainers</groupId>
             <artifactId>testcontainers</artifactId>
-            <version>1.2.1</version>
+            <version>${testcontainers.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/docker/DockerClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/docker/DockerClientTest.java
index f947d9e..fbc9fe1 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/docker/DockerClientTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/docker/DockerClientTest.java
@@ -4,53 +4,44 @@ import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.keycloak.common.Profile;
+import org.keycloak.representations.idm.KeysMetadataRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.ProfileAssume;
-import org.keycloak.testsuite.util.WaitUtils;
-import org.rnorth.ducttape.ratelimits.RateLimiterBuilder;
-import org.rnorth.ducttape.unreliables.Unreliables;
-import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testcontainers.containers.BindMode;
 import org.testcontainers.containers.Container;
 import org.testcontainers.containers.GenericContainer;
 import org.testcontainers.containers.output.Slf4jLogConsumer;
-import org.testcontainers.images.builder.ImageFromDockerfile;
-import org.testcontainers.shaded.com.github.dockerjava.api.model.ContainerNetwork;
+import sun.security.provider.X509Factory;
 
-import java.util.Arrays;
+import java.io.File;
+import java.io.PrintWriter;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Predicate;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
 import static org.junit.Assume.assumeTrue;
-import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
 
 public class DockerClientTest extends AbstractKeycloakTest {
-    public static final Logger LOGGER = LoggerFactory.getLogger(DockerClientTest.class);
-
     public static final String REALM_ID = "docker-test-realm";
-    public static final String AUTH_FLOW = "docker-basic-auth-flow";
     public static final String CLIENT_ID = "docker-test-client";
     public static final String DOCKER_USER = "docker-user";
     public static final String DOCKER_USER_PASSWORD = "password";
 
-    public static final String REGISTRY_HOSTNAME = "registry.localdomain";
+    public static final String REGISTRY_HOSTNAME = "localhost";
     public static final Integer REGISTRY_PORT = 5000;
     public static final String MINIMUM_DOCKER_VERSION = "1.8.0";
-    public static final String IMAGE_NAME = "busybox";
 
     private GenericContainer dockerRegistryContainer = null;
     private GenericContainer dockerClientContainer = null;
 
     private static String hostIp;
+    private static String authServerPort;
 
     @BeforeClass
     public static void verifyEnvironment() {
@@ -59,7 +50,6 @@ public class DockerClientTest extends AbstractKeycloakTest {
         final Optional<DockerVersion> dockerVersion = new DockerHostVersionSupplier().get();
         assumeTrue("Could not determine docker version for host machine.  It either is not present or accessible to the JVM running the test harness.", dockerVersion.isPresent());
         assumeTrue("Docker client on host machine is not a supported version.  Please upgrade and try again.", DockerVersion.COMPARATOR.compare(dockerVersion.get(), DockerVersion.parseVersionString(MINIMUM_DOCKER_VERSION)) >= 0);
-        LOGGER.debug("Discovered valid docker client on host.  version: {}", dockerVersion);
 
         hostIp = System.getProperty("host.ip");
 
@@ -70,21 +60,13 @@ public class DockerClientTest extends AbstractKeycloakTest {
             }
         }
         Assert.assertNotNull("Could not resolve host machine's IP address for docker adapter, and 'host.ip' system poperty not set. Client will not be able to authenticate against the keycloak server!", hostIp);
+
+        authServerPort = AUTH_SERVER_SSL_REQUIRED ? System.getProperty("auth.server.https.port") : System.getProperty("auth.server.http.port");
     }
 
     @Override
     public void addTestRealms(final List<RealmRepresentation> testRealms) {
-        final RealmRepresentation dockerRealm = loadJson(getClass().getResourceAsStream("/docker-test-realm.json"), RealmRepresentation.class);
-
-        /**
-         * TODO fix test harness/importer NPEs when attempting to create realm from scratch.
-         * Need to fix those, would be preferred to do this programmatically such that we don't have to keep realm elements
-         * (I.E. certs, realm url) in sync with a flat file
-         *
-         * final RealmRepresentation dockerRealm = DockerTestRealmSetup.createRealm(REALM_ID);
-         * DockerTestRealmSetup.configureDockerAuthenticationFlow(dockerRealm, AUTH_FLOW);
-         */
-
+        final RealmRepresentation dockerRealm = DockerTestRealmSetup.createRealm(REALM_ID);
         DockerTestRealmSetup.configureDockerRegistryClient(dockerRealm, CLIENT_ID);
         DockerTestRealmSetup.configureUser(dockerRealm, DOCKER_USER, DOCKER_USER_PASSWORD);
 
@@ -95,106 +77,71 @@ public class DockerClientTest extends AbstractKeycloakTest {
     public void beforeAbstractKeycloakTest() throws Exception {
         super.beforeAbstractKeycloakTest();
 
+        // find the realm cert
+        String realmCert = null;
+        List<KeysMetadataRepresentation.KeyMetadataRepresentation> realmKeys = adminClient.realm(REALM_ID).keys().getKeyMetadata().getKeys();
+        for (KeysMetadataRepresentation.KeyMetadataRepresentation key : realmKeys) {
+            if (key.getType().equals("RSA")) {
+                realmCert = key.getCertificate();
+            }
+        }
+        if (realmCert == null) {
+            throw new IllegalStateException("Cannot find public realm cert");
+        }
+
+        // save the cert to a file
+        File tmpCertFile = File.createTempFile("keycloak-docker-realm-cert-", ".pem");
+        tmpCertFile.deleteOnExit();
+        PrintWriter tmpCertWriter = new PrintWriter(tmpCertFile);
+        tmpCertWriter.println(X509Factory.BEGIN_CERT);
+        tmpCertWriter.println(realmCert);
+        tmpCertWriter.println(X509Factory.END_CERT);
+        tmpCertWriter.close();
+
         final Map<String, String> environment = new HashMap<>();
         environment.put("REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY", "/tmp");
-        environment.put("REGISTRY_HTTP_TLS_CERTIFICATE", "/opt/certs/localhost.crt");
-        environment.put("REGISTRY_HTTP_TLS_KEY", "/opt/certs/localhost.key");
-        environment.put("REGISTRY_AUTH_TOKEN_REALM", "http://" + hostIp + ":8180/auth/realms/docker-test-realm/protocol/docker-v2/auth");
+        environment.put("REGISTRY_AUTH_TOKEN_REALM", "http://" + hostIp + ":" + authServerPort + "/auth/realms/" + REALM_ID + "/protocol/docker-v2/auth");
         environment.put("REGISTRY_AUTH_TOKEN_SERVICE", CLIENT_ID);
-        environment.put("REGISTRY_AUTH_TOKEN_ISSUER", "http://" + hostIp + ":8180/auth/realms/docker-test-realm");
-        environment.put("REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE", "/opt/certs/docker-realm-public-key.pem");
+        environment.put("REGISTRY_AUTH_TOKEN_ISSUER", "http://" + hostIp + ":" + authServerPort + "/auth/realms/" + REALM_ID);
+        environment.put("REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE", "/opt/kc-certs/" + tmpCertFile.getCanonicalFile().getName());
         environment.put("INSECURE_REGISTRY", "--insecure-registry " + REGISTRY_HOSTNAME + ":" + REGISTRY_PORT);
 
         String dockerioPrefix = Boolean.parseBoolean(System.getProperty("docker.io-prefix-explicit")) ? "docker.io/" : "";
 
-        // TODO this required me to turn selinux off :(.  Add BindMode options for :z and :Z.  Make selinux enforcing again!
         dockerRegistryContainer = new GenericContainer(dockerioPrefix + "registry:2")
-                .withClasspathResourceMapping("dockerClientTest/keycloak-docker-compose-yaml/certs", "/opt/certs", BindMode.READ_ONLY)
+                .withFileSystemBind(tmpCertFile.getCanonicalPath(), "/opt/kc-certs/" + tmpCertFile.getCanonicalFile().getName(), BindMode.READ_ONLY)
                 .withEnv(environment)
+                .withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("dockerRegistryContainer")))
+                .withNetworkMode("host")
                 .withPrivilegedMode(true);
         dockerRegistryContainer.start();
-        dockerRegistryContainer.followOutput(new Slf4jLogConsumer(LOGGER));
-
-        dockerClientContainer = new GenericContainer(
-                new ImageFromDockerfile()
-                        .withDockerfileFromBuilder(dockerfileBuilder -> {
-                            dockerfileBuilder.from("centos/systemd:latest")
-                                    .run("yum", "install", "-y", "docker", "iptables", ";", "yum", "clean", "all")
-                                    .cmd("/usr/sbin/init")
-                                    .volume("/sys/fs/cgroup")
-                                    .build();
-                        })
-        )
-                .withClasspathResourceMapping("dockerClientTest/keycloak-docker-compose-yaml/certs/localhost.crt", "/opt/docker/certs.d/" + REGISTRY_HOSTNAME + "/localhost.crt", BindMode.READ_ONLY)
-                .withClasspathResourceMapping("dockerClientTest/keycloak-docker-compose-yaml/sysconfig_docker", "/etc/sysconfig/docker", BindMode.READ_WRITE)
-                .withPrivilegedMode(true);
-
-        final Optional<ContainerNetwork> network = dockerRegistryContainer.getContainerInfo().getNetworkSettings().getNetworks().values().stream().findFirst();
-        assumeTrue("Could not find a network adapter whereby the docker client container could connect to host!", network.isPresent());
-        dockerClientContainer.withExtraHost(REGISTRY_HOSTNAME, network.get().getIpAddress());
 
+        dockerClientContainer = new GenericContainer(dockerioPrefix + "docker:dind")
+                .withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("dockerClientContainer")))
+                .withNetworkMode("host")
+                .withPrivilegedMode(true);
         dockerClientContainer.start();
-        dockerClientContainer.followOutput(new Slf4jLogConsumer(LOGGER));
-
-        int i = 0;
-        String stdErr = "";
-        while (i++ < 30) {
-            log.infof("Trying to start docker service; attempt: %d", i);
-            stdErr = dockerClientContainer.execInContainer("systemctl", "start", "docker.service").getStderr();
-            if (stdErr.isEmpty()) {
-                break;
-            }
-            else {
-                log.info("systemctl failed: " + stdErr);
-            }
-            WaitUtils.pause(1000);
-        }
-
-        assumeTrue("Cannot start docker service!", stdErr.isEmpty());
-
-        log.info("Waiting for docker service...");
-        validateDockerStarted();
-        log.info("Docker service successfully started");
     }
 
-    private void validateDockerStarted() {
-        final Callable<Boolean> checkStrategy = () -> {
-            try {
-                final String commandResult = dockerClientContainer.execInContainer("docker", "ps").getStderr();
-                return !commandResult.contains("Cannot connect");
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            } catch (Exception e) {
-                return false;
-            }
-        };
+    @Override
+    public void afterAbstractKeycloakTest() {
+        super.afterAbstractKeycloakTest();
 
-        Unreliables.retryUntilTrue(30, TimeUnit.SECONDS, () -> RateLimiterBuilder.newBuilder().withRate(1, TimeUnit.SECONDS).withConstantThroughput().build().getWhenReady(() -> {
-            try {
-                return checkStrategy.call();
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
-        }));
+        pause(5000); // wait for the container logs
+
+        dockerClientContainer.close();
+        dockerRegistryContainer.close();
     }
 
     @Test
     public void shouldPerformDockerAuthAgainstRegistry() throws Exception {
+        log.info("Starting the attempt for login...");
         Container.ExecResult dockerLoginResult = dockerClientContainer.execInContainer("docker", "login", "-u", DOCKER_USER, "-p", DOCKER_USER_PASSWORD, REGISTRY_HOSTNAME + ":" + REGISTRY_PORT);
-        printNonEmpties(dockerLoginResult.getStdout(), dockerLoginResult.getStderr());
+        printCommandResult(dockerLoginResult);
         assertThat(dockerLoginResult.getStdout(), containsString("Login Succeeded"));
     }
 
-    private static void printNonEmpties(final String... results) {
-        Arrays.stream(results)
-                .forEachOrdered(DockerClientTest::printNonEmpty);
-    }
-
-    private static void printNonEmpty(final String result) {
-        if (nullOrEmpty.negate().test(result)) {
-            LOGGER.info(result);
-        }
+    private void printCommandResult(Container.ExecResult result) {
+        log.infof("Command executed. Output follows:\nSTDOUT: %s\n---\nSTDERR: %s", result.getStdout(), result.getStderr());
     }
-
-    public static final Predicate<String> nullOrEmpty = string -> string == null || string.isEmpty();
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/docker/DockerTestRealmSetup.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/docker/DockerTestRealmSetup.java
index 727af1d..6e711f0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/docker/DockerTestRealmSetup.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/docker/DockerTestRealmSetup.java
@@ -1,10 +1,6 @@
 package org.keycloak.testsuite.docker;
 
-import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.protocol.docker.DockerAuthV2Protocol;
-import org.keycloak.protocol.docker.DockerAuthenticator;
-import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
-import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
@@ -31,31 +27,6 @@ public final class DockerTestRealmSetup {
         return createdRealm;
     }
 
-    public static void configureDockerAuthenticationFlow(final RealmRepresentation dockerRealm, final String authFlowAlais) {
-        final AuthenticationFlowRepresentation dockerBasicAuthFlow = new AuthenticationFlowRepresentation();
-        dockerBasicAuthFlow.setId(UUID.randomUUID().toString());
-        dockerBasicAuthFlow.setAlias(authFlowAlais);
-        dockerBasicAuthFlow.setProviderId("basic-flow");
-        dockerBasicAuthFlow.setTopLevel(true);
-        dockerBasicAuthFlow.setBuiltIn(false);
-
-        final AuthenticationExecutionExportRepresentation dockerBasicAuthExecution = new AuthenticationExecutionExportRepresentation();
-        dockerBasicAuthExecution.setAuthenticator(DockerAuthenticator.ID);
-        dockerBasicAuthExecution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED.name());
-        dockerBasicAuthExecution.setPriority(0);
-        dockerBasicAuthExecution.setUserSetupAllowed(false);
-        dockerBasicAuthExecution.setAutheticatorFlow(false);
-
-        final List<AuthenticationExecutionExportRepresentation> authenticationExecutions = Optional.ofNullable(dockerBasicAuthFlow.getAuthenticationExecutions()).orElse(new ArrayList<>());
-        authenticationExecutions.add(dockerBasicAuthExecution);
-        dockerBasicAuthFlow.setAuthenticationExecutions(authenticationExecutions);
-
-        final List<AuthenticationFlowRepresentation> authenticationFlows = Optional.ofNullable(dockerRealm.getAuthenticationFlows()).orElse(new ArrayList<>());
-        authenticationFlows.add(dockerBasicAuthFlow);
-        dockerRealm.setAuthenticationFlows(authenticationFlows);
-        dockerRealm.setBrowserFlow(dockerBasicAuthFlow.getAlias());
-    }
-
 
     public static void configureDockerRegistryClient(final RealmRepresentation dockerRealm, final String clientId) {
         final ClientRepresentation dockerClient = new ClientRepresentation();