parent
4b36da03db
commit
819d33411a
9 changed files with 200 additions and 156 deletions
|
@ -20,6 +20,8 @@ package org.keycloak.operator.testsuite.integration;
|
||||||
import io.fabric8.kubernetes.api.model.HasMetadata;
|
import io.fabric8.kubernetes.api.model.HasMetadata;
|
||||||
import io.fabric8.kubernetes.api.model.NamespaceBuilder;
|
import io.fabric8.kubernetes.api.model.NamespaceBuilder;
|
||||||
import io.fabric8.kubernetes.api.model.Secret;
|
import io.fabric8.kubernetes.api.model.Secret;
|
||||||
|
import io.fabric8.kubernetes.api.model.PodSpecFluent.ContainersNested;
|
||||||
|
import io.fabric8.kubernetes.api.model.PodTemplateSpecFluent.SpecNested;
|
||||||
import io.fabric8.kubernetes.client.Config;
|
import io.fabric8.kubernetes.client.Config;
|
||||||
import io.fabric8.kubernetes.client.ConfigBuilder;
|
import io.fabric8.kubernetes.client.ConfigBuilder;
|
||||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||||
|
@ -41,6 +43,9 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.TestInfo;
|
import org.junit.jupiter.api.TestInfo;
|
||||||
import org.keycloak.operator.Constants;
|
import org.keycloak.operator.Constants;
|
||||||
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
||||||
|
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakSpecBuilder;
|
||||||
|
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakSpecFluent.UnsupportedNested;
|
||||||
|
import org.keycloak.operator.crds.v2alpha1.deployment.spec.UnsupportedSpecFluent.PodTemplateNested;
|
||||||
import org.keycloak.operator.testsuite.utils.K8sUtils;
|
import org.keycloak.operator.testsuite.utils.K8sUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -80,7 +85,6 @@ public abstract class BaseOperatorTest {
|
||||||
private static Operator operator;
|
private static Operator operator;
|
||||||
protected static boolean isOpenShift;
|
protected static boolean isOpenShift;
|
||||||
|
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void before() throws FileNotFoundException {
|
public static void before() throws FileNotFoundException {
|
||||||
configuration = CDI.current().select(QuarkusConfigurationService.class).get();
|
configuration = CDI.current().select(QuarkusConfigurationService.class).get();
|
||||||
|
@ -263,4 +267,45 @@ public abstract class BaseOperatorTest {
|
||||||
public static String getCurrentNamespace() {
|
public static String getCurrentNamespace() {
|
||||||
return namespace;
|
return namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getTestCustomImage() {
|
||||||
|
return customImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the default deployment modified/optimized by operator test settings
|
||||||
|
* @param disableProbes when true the unsupported template will be used to effectively
|
||||||
|
* disable the probes, which will speed up testing for scenarios that don't interact
|
||||||
|
* with the underlying keycloak
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Keycloak getTestKeycloakDeployment(boolean disableProbes) {
|
||||||
|
Keycloak kc = K8sUtils.getDefaultKeycloakDeployment();
|
||||||
|
kc.getMetadata().setNamespace(getCurrentNamespace());
|
||||||
|
String image = getTestCustomImage();
|
||||||
|
if (image != null) {
|
||||||
|
kc.getSpec().setImage(image);
|
||||||
|
}
|
||||||
|
if (disableProbes) {
|
||||||
|
return disableProbes(kc);
|
||||||
|
}
|
||||||
|
return kc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Keycloak disableProbes(Keycloak keycloak) {
|
||||||
|
KeycloakSpecBuilder specBuilder = new KeycloakSpecBuilder(keycloak.getSpec());
|
||||||
|
var podTemplateSpecBuilder = specBuilder.editOrNewUnsupported().editOrNewPodTemplate().editOrNewSpec();
|
||||||
|
ContainersNested<SpecNested<PodTemplateNested<UnsupportedNested<KeycloakSpecBuilder>>>> containerBuilder = null;
|
||||||
|
if (podTemplateSpecBuilder.hasContainers()) {
|
||||||
|
containerBuilder = podTemplateSpecBuilder.editContainer(0);
|
||||||
|
} else {
|
||||||
|
containerBuilder = podTemplateSpecBuilder.addNewContainer();
|
||||||
|
}
|
||||||
|
keycloak.setSpec(containerBuilder.withNewLivenessProbe().withNewExec().addToCommand("true").endExec()
|
||||||
|
.endLivenessProbe().withNewReadinessProbe().withNewExec().addToCommand("true").endExec()
|
||||||
|
.endReadinessProbe().withNewStartupProbe().withNewExec().addToCommand("true").endExec()
|
||||||
|
.endStartupProbe().endContainer().endSpec().endPodTemplate().endUnsupported().build());
|
||||||
|
return keycloak;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,11 +54,11 @@ public class ClusteringTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testMultipleDeployments() throws InterruptedException {
|
public void testMultipleDeployments() throws InterruptedException {
|
||||||
// given
|
// given
|
||||||
var kc = K8sUtils.getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
|
|
||||||
// another instance running off the same database
|
// another instance running off the same database
|
||||||
// - should eventually give this a separate schema
|
// - should eventually give this a separate schema
|
||||||
var kc1 = K8sUtils.getDefaultKeycloakDeployment();
|
var kc1 = getTestKeycloakDeployment(true);
|
||||||
kc1.getMetadata().setName("another-example");
|
kc1.getMetadata().setName("another-example");
|
||||||
kc1.getSpec().getHostnameSpec().setHostname("another-example.com");
|
kc1.getSpec().getHostnameSpec().setHostname("another-example.com");
|
||||||
// this is using the wrong tls-secret, but simply removing http spec renders the pod unstartable
|
// this is using the wrong tls-secret, but simply removing http spec renders the pod unstartable
|
||||||
|
@ -109,7 +109,7 @@ public class ClusteringTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testKeycloakScaleAsExpected() {
|
public void testKeycloakScaleAsExpected() {
|
||||||
// given
|
// given
|
||||||
var kc = K8sUtils.getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(false);
|
||||||
var crSelector = k8sclient.resource(kc);
|
var crSelector = k8sclient.resource(kc);
|
||||||
K8sUtils.deployKeycloak(k8sclient, kc, true);
|
K8sUtils.deployKeycloak(k8sclient, kc, true);
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ public class ClusteringTest extends BaseOperatorTest {
|
||||||
public void testKeycloakCacheIsConnected() throws Exception {
|
public void testKeycloakCacheIsConnected() throws Exception {
|
||||||
// given
|
// given
|
||||||
Log.info("Setup");
|
Log.info("Setup");
|
||||||
var kc = K8sUtils.getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(false);
|
||||||
var crSelector = k8sclient.resource(kc);
|
var crSelector = k8sclient.resource(kc);
|
||||||
K8sUtils.deployKeycloak(k8sclient, kc, false);
|
K8sUtils.deployKeycloak(k8sclient, kc, false);
|
||||||
var targetInstances = 3;
|
var targetInstances = 3;
|
||||||
|
|
|
@ -62,7 +62,6 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.keycloak.operator.testsuite.utils.CRAssert.assertKeycloakStatusCondition;
|
import static org.keycloak.operator.testsuite.utils.CRAssert.assertKeycloakStatusCondition;
|
||||||
import static org.keycloak.operator.testsuite.utils.K8sUtils.deployKeycloak;
|
import static org.keycloak.operator.testsuite.utils.K8sUtils.deployKeycloak;
|
||||||
import static org.keycloak.operator.testsuite.utils.K8sUtils.getDefaultKeycloakDeployment;
|
|
||||||
import static org.keycloak.operator.testsuite.utils.K8sUtils.getResourceFromFile;
|
import static org.keycloak.operator.testsuite.utils.K8sUtils.getResourceFromFile;
|
||||||
import static org.keycloak.operator.testsuite.utils.K8sUtils.waitForKeycloakToBeReady;
|
import static org.keycloak.operator.testsuite.utils.K8sUtils.waitForKeycloakToBeReady;
|
||||||
|
|
||||||
|
@ -73,7 +72,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
try {
|
try {
|
||||||
// CR
|
// CR
|
||||||
Log.info("Creating new Keycloak CR example");
|
Log.info("Creating new Keycloak CR example");
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
var deploymentName = kc.getMetadata().getName();
|
var deploymentName = kc.getMetadata().getName();
|
||||||
deployKeycloak(k8sclient, kc, true);
|
deployKeycloak(k8sclient, kc, true);
|
||||||
|
|
||||||
|
@ -103,7 +102,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testCRFields() {
|
public void testCRFields() {
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
var deploymentName = kc.getMetadata().getName();
|
var deploymentName = kc.getMetadata().getName();
|
||||||
deployKeycloak(k8sclient, kc, true);
|
deployKeycloak(k8sclient, kc, true);
|
||||||
|
|
||||||
|
@ -137,7 +136,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testConfigInCRTakesPrecedence() {
|
public void testConfigInCRTakesPrecedence() {
|
||||||
try {
|
try {
|
||||||
var defaultKCDeploy = getDefaultKeycloakDeployment();
|
var defaultKCDeploy = getTestKeycloakDeployment(true);
|
||||||
|
|
||||||
var valueSecretHealthProp = new ValueOrSecret("health-enabled", "false");
|
var valueSecretHealthProp = new ValueOrSecret("health-enabled", "false");
|
||||||
var valueSecretProxyProp = new ValueOrSecret("proxy", "reencrypt");
|
var valueSecretProxyProp = new ValueOrSecret("proxy", "reencrypt");
|
||||||
|
@ -209,7 +208,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testDeploymentDurability() {
|
public void testDeploymentDurability() {
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
var deploymentName = kc.getMetadata().getName();
|
var deploymentName = kc.getMetadata().getName();
|
||||||
|
|
||||||
// create a dummy StatefulSet representing the pre-multiinstance state that we'll be forced to delete
|
// create a dummy StatefulSet representing the pre-multiinstance state that we'll be forced to delete
|
||||||
|
@ -274,7 +273,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testTlsUsesCorrectSecret() {
|
public void testTlsUsesCorrectSecret() {
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
deployKeycloak(k8sclient, kc, true);
|
deployKeycloak(k8sclient, kc, true);
|
||||||
|
|
||||||
var service = new KeycloakService(k8sclient, kc);
|
var service = new KeycloakService(k8sclient, kc);
|
||||||
|
@ -284,10 +283,10 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
String url = "https://" + service.getName() + "." + namespace + ":" + Constants.KEYCLOAK_HTTPS_PORT;
|
String url = "https://" + service.getName() + "." + namespace + ":" + Constants.KEYCLOAK_HTTPS_PORT;
|
||||||
Log.info("Checking url: " + url);
|
Log.info("Checking url: " + url);
|
||||||
|
|
||||||
var curlOutput = K8sUtils.inClusterCurl(k8sclient, namespace, "--insecure", "-s", "-v", url);
|
var curlOutput = K8sUtils.inClusterCurl(k8sclient, namespace, "--insecure", "-s", "-w", "%{certs}", url);
|
||||||
Log.info("Curl Output: " + curlOutput);
|
Log.info("Curl Output: " + curlOutput);
|
||||||
|
|
||||||
assertTrue(curlOutput.contains("issuer: O=mkcert development CA; OU=aperuffo@aperuffo-mac (Andrea Peruffo); CN=mkcert aperuffo@aperuffo-mac (Andrea Peruffo)"));
|
assertTrue(curlOutput.contains("Issuer:O = mkcert development CA, OU = aperuffo@aperuffo-mac (Andrea Peruffo), CN = mkcert aperuffo@aperuffo-mac (Andrea Peruffo)"));
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
savePodLogs();
|
savePodLogs();
|
||||||
|
@ -298,7 +297,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testTlsDisabled() {
|
public void testTlsDisabled() {
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
kc.getSpec().getHttpSpec().setTlsSecret(null);
|
kc.getSpec().getHttpSpec().setTlsSecret(null);
|
||||||
kc.getSpec().getHttpSpec().setHttpEnabled(true);
|
kc.getSpec().getHttpSpec().setHttpEnabled(true);
|
||||||
deployKeycloak(k8sclient, kc, true);
|
deployKeycloak(k8sclient, kc, true);
|
||||||
|
@ -313,7 +312,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testHostnameStrict() {
|
public void testHostnameStrict() {
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
deployKeycloak(k8sclient, kc, true);
|
deployKeycloak(k8sclient, kc, true);
|
||||||
|
|
||||||
var service = new KeycloakService(k8sclient, kc);
|
var service = new KeycloakService(k8sclient, kc);
|
||||||
|
@ -337,7 +336,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testHostnameStrictDisabled() {
|
public void testHostnameStrictDisabled() {
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
var hostnameSpec = new HostnameSpecBuilder()
|
var hostnameSpec = new HostnameSpecBuilder()
|
||||||
.withStrict(false)
|
.withStrict(false)
|
||||||
.withStrictBackchannel(false)
|
.withStrictBackchannel(false)
|
||||||
|
@ -369,7 +368,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
try {
|
try {
|
||||||
final int httpsPort = 8543;
|
final int httpsPort = 8543;
|
||||||
final int httpPort = 8180;
|
final int httpPort = 8180;
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
kc.getSpec().getHttpSpec().setHttpsPort(httpsPort);
|
kc.getSpec().getHttpSpec().setHttpsPort(httpsPort);
|
||||||
kc.getSpec().getHttpSpec().setHttpPort(httpPort);
|
kc.getSpec().getHttpSpec().setHttpPort(httpPort);
|
||||||
|
|
||||||
|
@ -393,7 +392,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
try {
|
try {
|
||||||
final int httpsPort = 8543;
|
final int httpsPort = 8543;
|
||||||
final int httpPort = 8180;
|
final int httpPort = 8180;
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
kc.getSpec().getHttpSpec().setHttpsPort(httpsPort);
|
kc.getSpec().getHttpSpec().setHttpsPort(httpsPort);
|
||||||
kc.getSpec().getHttpSpec().setHttpPort(httpPort);
|
kc.getSpec().getHttpSpec().setHttpPort(httpPort);
|
||||||
kc.getSpec().getHttpSpec().setTlsSecret(null);
|
kc.getSpec().getHttpSpec().setTlsSecret(null);
|
||||||
|
@ -419,7 +418,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testInitialAdminUser() {
|
public void testInitialAdminUser() {
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
var kcAdminSecret = new KeycloakAdminSecret(k8sclient, kc);
|
var kcAdminSecret = new KeycloakAdminSecret(k8sclient, kc);
|
||||||
|
|
||||||
k8sclient
|
k8sclient
|
||||||
|
@ -510,7 +509,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
@EnabledIfSystemProperty(named = OPERATOR_CUSTOM_IMAGE, matches = ".+")
|
@EnabledIfSystemProperty(named = OPERATOR_CUSTOM_IMAGE, matches = ".+")
|
||||||
public void testCustomImage() {
|
public void testCustomImage() {
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
kc.getSpec().setImage(customImage);
|
kc.getSpec().setImage(customImage);
|
||||||
deployKeycloak(k8sclient, kc, true);
|
deployKeycloak(k8sclient, kc, true);
|
||||||
|
|
||||||
|
@ -535,7 +534,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
String secretDescriptorFilename = "test-docker-registry-secret.yaml";
|
String secretDescriptorFilename = "test-docker-registry-secret.yaml";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
kc.getSpec().setImage(customImage);
|
kc.getSpec().setImage(customImage);
|
||||||
|
|
||||||
handleFakeImagePullSecretCreation(kc, secretDescriptorFilename);
|
handleFakeImagePullSecretCreation(kc, secretDescriptorFilename);
|
||||||
|
@ -563,7 +562,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
public void testInvalidCustomImageHasErrorMessage() {
|
public void testInvalidCustomImageHasErrorMessage() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
kc.getSpec().setImage("does-not-exist");
|
kc.getSpec().setImage("does-not-exist");
|
||||||
|
|
||||||
deployKeycloak(k8sclient, kc, false);
|
deployKeycloak(k8sclient, kc, false);
|
||||||
|
@ -587,7 +586,8 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testHttpRelativePathWithPlainValue() {
|
public void testHttpRelativePathWithPlainValue() {
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(false);
|
||||||
|
kc.getSpec().setImage(null); // doesn't seem to become ready with the custom image
|
||||||
kc.getSpec().getAdditionalOptions().add(new ValueOrSecret(Constants.KEYCLOAK_HTTP_RELATIVE_PATH_KEY, "/foobar"));
|
kc.getSpec().getAdditionalOptions().add(new ValueOrSecret(Constants.KEYCLOAK_HTTP_RELATIVE_PATH_KEY, "/foobar"));
|
||||||
deployKeycloak(k8sclient, kc, true);
|
deployKeycloak(k8sclient, kc, true);
|
||||||
|
|
||||||
|
@ -608,7 +608,8 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testHttpRelativePathWithSecretValue() {
|
public void testHttpRelativePathWithSecretValue() {
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(false);
|
||||||
|
kc.getSpec().setImage(null); // doesn't seem to become ready with the custom image
|
||||||
var secretName = "my-http-relative-path";
|
var secretName = "my-http-relative-path";
|
||||||
var keyName = "rel-path";
|
var keyName = "rel-path";
|
||||||
var httpRelativePathSecret = new SecretBuilder()
|
var httpRelativePathSecret = new SecretBuilder()
|
||||||
|
@ -644,7 +645,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testUpgradeRecreatesPods() {
|
public void testUpgradeRecreatesPods() {
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
kc.getSpec().setInstances(3);
|
kc.getSpec().setInstances(3);
|
||||||
deployKeycloak(k8sclient, kc, true);
|
deployKeycloak(k8sclient, kc, true);
|
||||||
|
|
||||||
|
@ -691,7 +692,7 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
|
||||||
"Skipping the test when Operator deployed remotely to keep stuff simple, it's just SmallRye, we don't need to retest it");
|
"Skipping the test when Operator deployed remotely to keep stuff simple, it's just SmallRye, we don't need to retest it");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
deployKeycloak(k8sclient, kc, true);
|
deployKeycloak(k8sclient, kc, true);
|
||||||
|
|
||||||
// labels are set in test/resources/application.properties
|
// labels are set in test/resources/application.properties
|
||||||
|
|
|
@ -63,7 +63,7 @@ public class KeycloakIngressTest extends BaseOperatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIngressOnHTTP() {
|
public void testIngressOnHTTP() {
|
||||||
var kc = K8sUtils.getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(false);
|
||||||
kc.getSpec().getHttpSpec().setTlsSecret(null);
|
kc.getSpec().getHttpSpec().setTlsSecret(null);
|
||||||
kc.getSpec().getHttpSpec().setHttpEnabled(true);
|
kc.getSpec().getHttpSpec().setHttpEnabled(true);
|
||||||
var hostnameSpecBuilder = new HostnameSpecBuilder()
|
var hostnameSpecBuilder = new HostnameSpecBuilder()
|
||||||
|
@ -89,7 +89,7 @@ public class KeycloakIngressTest extends BaseOperatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIngressOnHTTPS() {
|
public void testIngressOnHTTPS() {
|
||||||
var kc = K8sUtils.getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(false);
|
||||||
var hostnameSpecBuilder = new HostnameSpecBuilder()
|
var hostnameSpecBuilder = new HostnameSpecBuilder()
|
||||||
.withStrict(false)
|
.withStrict(false)
|
||||||
.withStrictBackchannel(false);
|
.withStrictBackchannel(false);
|
||||||
|
@ -142,7 +142,7 @@ public class KeycloakIngressTest extends BaseOperatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIngressHostname() {
|
public void testIngressHostname() {
|
||||||
var kc = K8sUtils.getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
var hostnameSpec = new HostnameSpecBuilder().withHostname("foo.bar").build();
|
var hostnameSpec = new HostnameSpecBuilder().withHostname("foo.bar").build();
|
||||||
kc.getSpec().setHostnameSpec(hostnameSpec);
|
kc.getSpec().setHostnameSpec(hostnameSpec);
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ public class KeycloakIngressTest extends BaseOperatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMainIngressDurability() {
|
public void testMainIngressDurability() {
|
||||||
var kc = K8sUtils.getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
kc.getSpec().setIngressSpec(new IngressSpec());
|
kc.getSpec().setIngressSpec(new IngressSpec());
|
||||||
kc.getSpec().getIngressSpec().setIngressEnabled(true);
|
kc.getSpec().getIngressSpec().setIngressEnabled(true);
|
||||||
kc.getSpec().getIngressSpec().setAnnotations(Map.of("haproxy.router.openshift.io/disable_cookies", "true"));
|
kc.getSpec().getIngressSpec().setAnnotations(Map.of("haproxy.router.openshift.io/disable_cookies", "true"));
|
||||||
|
@ -226,8 +226,7 @@ public class KeycloakIngressTest extends BaseOperatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCustomIngressDeletion() {
|
public void testCustomIngressDeletion() {
|
||||||
|
Keycloak defaultKeycloakDeployment = getTestKeycloakDeployment(true);
|
||||||
Keycloak defaultKeycloakDeployment = K8sUtils.getDefaultKeycloakDeployment();
|
|
||||||
String kcDeploymentName = defaultKeycloakDeployment.getMetadata().getName();
|
String kcDeploymentName = defaultKeycloakDeployment.getMetadata().getName();
|
||||||
Resource<Ingress> customIngressDeployedManuallySelector = null;
|
Resource<Ingress> customIngressDeployedManuallySelector = null;
|
||||||
Ingress customIngressCreatedManually;
|
Ingress customIngressCreatedManually;
|
||||||
|
@ -271,10 +270,10 @@ public class KeycloakIngressTest extends BaseOperatorTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCustomIngressClassName() {
|
public void testCustomIngressClassName() {
|
||||||
var kc = K8sUtils.getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
kc.getSpec().setIngressSpec(new IngressSpecBuilder().withIngressClassName("nginx").build());
|
kc.getSpec().setIngressSpec(new IngressSpecBuilder().withIngressClassName("nginx").build());
|
||||||
K8sUtils.deployKeycloak(k8sclient, kc, true);
|
K8sUtils.deployKeycloak(k8sclient, kc, true);
|
||||||
|
|
||||||
|
@ -307,7 +306,7 @@ public class KeycloakIngressTest extends BaseOperatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCustomIngressAnnotations() {
|
public void testCustomIngressAnnotations() {
|
||||||
var kc = K8sUtils.getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
kc.getSpec().setIngressSpec(new IngressSpec());
|
kc.getSpec().setIngressSpec(new IngressSpec());
|
||||||
kc.getSpec().getIngressSpec().setIngressEnabled(true);
|
kc.getSpec().getIngressSpec().setIngressEnabled(true);
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
public class KeycloakServicesTest extends BaseOperatorTest {
|
public class KeycloakServicesTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testMainServiceDurability() {
|
public void testMainServiceDurability() {
|
||||||
var kc = K8sUtils.getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
K8sUtils.deployKeycloak(k8sclient, kc, true);
|
K8sUtils.deployKeycloak(k8sclient, kc, true);
|
||||||
var service = new KeycloakService(k8sclient, kc);
|
var service = new KeycloakService(k8sclient, kc);
|
||||||
var serviceSelector = k8sclient.services().inNamespace(namespace).withName(service.getName());
|
var serviceSelector = k8sclient.services().inNamespace(namespace).withName(service.getName());
|
||||||
|
@ -84,7 +84,7 @@ public class KeycloakServicesTest extends BaseOperatorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDiscoveryServiceDurability() {
|
public void testDiscoveryServiceDurability() {
|
||||||
var kc = K8sUtils.getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
K8sUtils.deployKeycloak(k8sclient, kc, true);
|
K8sUtils.deployKeycloak(k8sclient, kc, true);
|
||||||
var discoveryService = new KeycloakDiscoveryService(k8sclient, kc);
|
var discoveryService = new KeycloakDiscoveryService(k8sclient, kc);
|
||||||
var discoveryServiceSelector = k8sclient.services().inNamespace(namespace).withName(discoveryService.getName());
|
var discoveryServiceSelector = k8sclient.services().inNamespace(namespace).withName(discoveryService.getName());
|
||||||
|
|
|
@ -40,7 +40,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.keycloak.operator.Constants.KEYCLOAK_HTTPS_PORT;
|
import static org.keycloak.operator.Constants.KEYCLOAK_HTTPS_PORT;
|
||||||
import static org.keycloak.operator.controllers.KeycloakDistConfigurator.getKeycloakOptionEnvVarName;
|
import static org.keycloak.operator.controllers.KeycloakDistConfigurator.getKeycloakOptionEnvVarName;
|
||||||
import static org.keycloak.operator.testsuite.utils.K8sUtils.deployKeycloak;
|
import static org.keycloak.operator.testsuite.utils.K8sUtils.deployKeycloak;
|
||||||
import static org.keycloak.operator.testsuite.utils.K8sUtils.getDefaultKeycloakDeployment;
|
|
||||||
import static org.keycloak.operator.testsuite.utils.K8sUtils.inClusterCurl;
|
import static org.keycloak.operator.testsuite.utils.K8sUtils.inClusterCurl;
|
||||||
import static org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImportStatusCondition.DONE;
|
import static org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImportStatusCondition.DONE;
|
||||||
import static org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImportStatusCondition.HAS_ERRORS;
|
import static org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImportStatusCondition.HAS_ERRORS;
|
||||||
|
@ -81,7 +80,8 @@ public class RealmImportTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testWorkingRealmImport() {
|
public void testWorkingRealmImport() {
|
||||||
// Arrange
|
// Arrange
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(false);
|
||||||
|
kc.getSpec().setImage(null); // checks the job args for the base, not custom image
|
||||||
kc.getSpec().setImagePullSecrets(Arrays.asList(new LocalObjectReferenceBuilder().withName("my-empty-secret").build()));
|
kc.getSpec().setImagePullSecrets(Arrays.asList(new LocalObjectReferenceBuilder().withName("my-empty-secret").build()));
|
||||||
deployKeycloak(k8sclient, kc, false);
|
deployKeycloak(k8sclient, kc, false);
|
||||||
|
|
||||||
|
@ -98,9 +98,10 @@ public class RealmImportTest extends BaseOperatorTest {
|
||||||
.pollDelay(1, SECONDS)
|
.pollDelay(1, SECONDS)
|
||||||
.ignoreExceptions()
|
.ignoreExceptions()
|
||||||
.untilAsserted(() -> {
|
.untilAsserted(() -> {
|
||||||
CRAssert.assertKeycloakRealmImportStatusCondition(crSelector.get(), DONE, false);
|
KeycloakRealmImport cr = crSelector.get();
|
||||||
CRAssert.assertKeycloakRealmImportStatusCondition(crSelector.get(), STARTED, true);
|
CRAssert.assertKeycloakRealmImportStatusCondition(cr, DONE, false);
|
||||||
CRAssert.assertKeycloakRealmImportStatusCondition(crSelector.get(), HAS_ERRORS, false);
|
CRAssert.assertKeycloakRealmImportStatusCondition(cr, STARTED, true);
|
||||||
|
CRAssert.assertKeycloakRealmImportStatusCondition(cr, HAS_ERRORS, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
Awaitility.await()
|
Awaitility.await()
|
||||||
|
@ -108,9 +109,10 @@ public class RealmImportTest extends BaseOperatorTest {
|
||||||
.pollDelay(1, SECONDS)
|
.pollDelay(1, SECONDS)
|
||||||
.ignoreExceptions()
|
.ignoreExceptions()
|
||||||
.untilAsserted(() -> {
|
.untilAsserted(() -> {
|
||||||
CRAssert.assertKeycloakRealmImportStatusCondition(crSelector.get(), DONE, true);
|
KeycloakRealmImport cr = crSelector.get();
|
||||||
CRAssert.assertKeycloakRealmImportStatusCondition(crSelector.get(), STARTED, false);
|
CRAssert.assertKeycloakRealmImportStatusCondition(cr, DONE, true);
|
||||||
CRAssert.assertKeycloakRealmImportStatusCondition(crSelector.get(), HAS_ERRORS, false);
|
CRAssert.assertKeycloakRealmImportStatusCondition(cr, STARTED, false);
|
||||||
|
CRAssert.assertKeycloakRealmImportStatusCondition(cr, HAS_ERRORS, false);
|
||||||
});
|
});
|
||||||
var job = k8sclient.batch().v1().jobs().inNamespace(namespace).withName("example-count0-kc").get();
|
var job = k8sclient.batch().v1().jobs().inNamespace(namespace).withName("example-count0-kc").get();
|
||||||
assertThat(job.getSpec().getTemplate().getMetadata().getLabels().get("app")).isEqualTo("keycloak-realm-import");
|
assertThat(job.getSpec().getTemplate().getMetadata().getLabels().get("app")).isEqualTo("keycloak-realm-import");
|
||||||
|
@ -120,11 +122,10 @@ public class RealmImportTest extends BaseOperatorTest {
|
||||||
assertThat(job.getSpec().getTemplate().getSpec().getImagePullSecrets().size()).isEqualTo(1);
|
assertThat(job.getSpec().getTemplate().getSpec().getImagePullSecrets().size()).isEqualTo(1);
|
||||||
assertThat(job.getSpec().getTemplate().getSpec().getImagePullSecrets().get(0).getName()).isEqualTo("my-empty-secret");
|
assertThat(job.getSpec().getTemplate().getSpec().getImagePullSecrets().get(0).getName()).isEqualTo("my-empty-secret");
|
||||||
|
|
||||||
var service = new KeycloakService(k8sclient, getDefaultKeycloakDeployment());
|
|
||||||
String url =
|
String url =
|
||||||
"https://" + service.getName() + "." + namespace + ":" + KEYCLOAK_HTTPS_PORT + "/realms/count0";
|
"https://" + KeycloakService.getServiceName(kc) + "." + namespace + ":" + KEYCLOAK_HTTPS_PORT + "/realms/count0";
|
||||||
|
|
||||||
Awaitility.await().atMost(10, MINUTES).untilAsserted(() -> {
|
Awaitility.await().atMost(10, MINUTES).ignoreExceptions().untilAsserted(() -> {
|
||||||
Log.info("Starting curl Pod to test if the realm is available");
|
Log.info("Starting curl Pod to test if the realm is available");
|
||||||
Log.info("Url: '" + url + "'");
|
Log.info("Url: '" + url + "'");
|
||||||
String curlOutput = inClusterCurl(k8sclient, namespace, url);
|
String curlOutput = inClusterCurl(k8sclient, namespace, url);
|
||||||
|
@ -139,7 +140,7 @@ public class RealmImportTest extends BaseOperatorTest {
|
||||||
@EnabledIfSystemProperty(named = OPERATOR_CUSTOM_IMAGE, matches = ".+")
|
@EnabledIfSystemProperty(named = OPERATOR_CUSTOM_IMAGE, matches = ".+")
|
||||||
public void testWorkingRealmImportWithCustomImage() {
|
public void testWorkingRealmImportWithCustomImage() {
|
||||||
// Arrange
|
// Arrange
|
||||||
var keycloak = getDefaultKeycloakDeployment();
|
var keycloak = getTestKeycloakDeployment(false);
|
||||||
keycloak.getSpec().setImage(customImage);
|
keycloak.getSpec().setImage(customImage);
|
||||||
// Removing the Database so that a subsequent build will by default act on h2
|
// Removing the Database so that a subsequent build will by default act on h2
|
||||||
// TODO: uncomment the following line after resolution of: https://github.com/keycloak/keycloak/issues/11767
|
// TODO: uncomment the following line after resolution of: https://github.com/keycloak/keycloak/issues/11767
|
||||||
|
@ -160,9 +161,10 @@ public class RealmImportTest extends BaseOperatorTest {
|
||||||
.pollDelay(5, SECONDS)
|
.pollDelay(5, SECONDS)
|
||||||
.ignoreExceptions()
|
.ignoreExceptions()
|
||||||
.untilAsserted(() -> {
|
.untilAsserted(() -> {
|
||||||
CRAssert.assertKeycloakRealmImportStatusCondition(crSelector.get(), DONE, true);
|
KeycloakRealmImport cr = crSelector.get();
|
||||||
CRAssert.assertKeycloakRealmImportStatusCondition(crSelector.get(), STARTED, false);
|
CRAssert.assertKeycloakRealmImportStatusCondition(cr, DONE, true);
|
||||||
CRAssert.assertKeycloakRealmImportStatusCondition(crSelector.get(), HAS_ERRORS, false);
|
CRAssert.assertKeycloakRealmImportStatusCondition(cr, STARTED, false);
|
||||||
|
CRAssert.assertKeycloakRealmImportStatusCondition(cr, HAS_ERRORS, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
assertThat(getJobArgs()).doesNotContain("build");
|
assertThat(getJobArgs()).doesNotContain("build");
|
||||||
|
@ -171,7 +173,7 @@ public class RealmImportTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testNotWorkingRealmImport() {
|
public void testNotWorkingRealmImport() {
|
||||||
// Arrange
|
// Arrange
|
||||||
deployKeycloak(k8sclient, getDefaultKeycloakDeployment(), true); // make sure there are no errors due to missing KC Deployment
|
deployKeycloak(k8sclient, getTestKeycloakDeployment(false), true); // make sure there are no errors due to missing KC Deployment
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
K8sUtils.set(k8sclient, getClass().getResourceAsStream("/incorrect-realm.yaml"));
|
K8sUtils.set(k8sclient, getClass().getResourceAsStream("/incorrect-realm.yaml"));
|
||||||
|
@ -186,10 +188,10 @@ public class RealmImportTest extends BaseOperatorTest {
|
||||||
.resources(KeycloakRealmImport.class)
|
.resources(KeycloakRealmImport.class)
|
||||||
.inNamespace(namespace)
|
.inNamespace(namespace)
|
||||||
.withName("example-count0-kc");
|
.withName("example-count0-kc");
|
||||||
|
KeycloakRealmImport cr = crSelector.get();
|
||||||
CRAssert.assertKeycloakRealmImportStatusCondition(crSelector.get(), DONE, false);
|
CRAssert.assertKeycloakRealmImportStatusCondition(cr, DONE, false);
|
||||||
CRAssert.assertKeycloakRealmImportStatusCondition(crSelector.get(), STARTED, false);
|
CRAssert.assertKeycloakRealmImportStatusCondition(cr, STARTED, false);
|
||||||
CRAssert.assertKeycloakRealmImportStatusCondition(crSelector.get(), HAS_ERRORS, true);
|
CRAssert.assertKeycloakRealmImportStatusCondition(cr, HAS_ERRORS, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,13 @@
|
||||||
|
|
||||||
package org.keycloak.operator.testsuite.integration;
|
package org.keycloak.operator.testsuite.integration;
|
||||||
|
|
||||||
|
import io.fabric8.kubernetes.api.model.Pod;
|
||||||
import io.fabric8.kubernetes.api.model.Secret;
|
import io.fabric8.kubernetes.api.model.Secret;
|
||||||
import io.fabric8.kubernetes.api.model.SecretBuilder;
|
import io.fabric8.kubernetes.api.model.SecretBuilder;
|
||||||
|
import io.fabric8.kubernetes.api.model.apps.StatefulSet;
|
||||||
import io.quarkus.logging.Log;
|
import io.quarkus.logging.Log;
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
|
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -31,18 +34,19 @@ import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusCondition;
|
||||||
import org.keycloak.operator.crds.v2alpha1.deployment.ValueOrSecret;
|
import org.keycloak.operator.crds.v2alpha1.deployment.ValueOrSecret;
|
||||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.HostnameSpecBuilder;
|
import org.keycloak.operator.crds.v2alpha1.deployment.spec.HostnameSpecBuilder;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.keycloak.operator.testsuite.utils.CRAssert.assertKeycloakStatusCondition;
|
import static org.keycloak.operator.testsuite.utils.CRAssert.assertKeycloakStatusCondition;
|
||||||
import static org.keycloak.operator.testsuite.utils.K8sUtils.deployKeycloak;
|
import static org.keycloak.operator.testsuite.utils.K8sUtils.deployKeycloak;
|
||||||
import static org.keycloak.operator.testsuite.utils.K8sUtils.getDefaultKeycloakDeployment;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||||
|
@ -52,7 +56,7 @@ public class WatchedSecretsTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testSecretsAreWatched() {
|
public void testSecretsAreWatched() {
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
deployKeycloak(k8sclient, kc, true);
|
deployKeycloak(k8sclient, kc, true);
|
||||||
|
|
||||||
Secret dbSecret = getDbSecret();
|
Secret dbSecret = getDbSecret();
|
||||||
|
@ -89,10 +93,10 @@ public class WatchedSecretsTest extends BaseOperatorTest {
|
||||||
try {
|
try {
|
||||||
final String username = "HomerSimpson";
|
final String username = "HomerSimpson";
|
||||||
|
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(false);
|
||||||
deployKeycloak(k8sclient, kc, true);
|
deployKeycloak(k8sclient, kc, true);
|
||||||
|
|
||||||
var prevPodNames = getPodNamesForCrs(Set.of(kc));
|
var prevRevision = getStatefulSet(kc).getStatus().getUpdateRevision();
|
||||||
|
|
||||||
var dbSecret = getDbSecret();
|
var dbSecret = getDbSecret();
|
||||||
|
|
||||||
|
@ -100,40 +104,29 @@ public class WatchedSecretsTest extends BaseOperatorTest {
|
||||||
Base64.getEncoder().encodeToString(username.getBytes()));
|
Base64.getEncoder().encodeToString(username.getBytes()));
|
||||||
k8sclient.resource(dbSecret).update();
|
k8sclient.resource(dbSecret).update();
|
||||||
|
|
||||||
Awaitility.await()
|
Pod pod = k8sclient.pods().withName(kc.getMetadata().getName() + "-0").waitUntilCondition(
|
||||||
.ignoreExceptions()
|
p -> p != null && !prevRevision.equals(p.getMetadata().getLabels().get("controller-revision-hash")),
|
||||||
.untilAsserted(() -> {
|
30, TimeUnit.SECONDS);
|
||||||
Log.info("Checking pod logs for DB auth failures");
|
|
||||||
var podlogs = getPodNamesForCrs(Set.of(kc)).stream()
|
|
||||||
.filter(n -> !prevPodNames.contains(n)) // checking just new pods
|
|
||||||
.map(n -> {
|
|
||||||
var name = k8sclient
|
|
||||||
.pods()
|
|
||||||
.inNamespace(namespace)
|
|
||||||
.list()
|
|
||||||
.getItems()
|
|
||||||
.stream()
|
|
||||||
.filter(p -> (p.getMetadata().getName() + p.getMetadata().getCreationTimestamp()).equals(n))
|
|
||||||
.findAny()
|
|
||||||
.get()
|
|
||||||
.getMetadata()
|
|
||||||
.getName();
|
|
||||||
|
|
||||||
return k8sclient.pods().inNamespace(namespace).withName(name).getLog();
|
ByteArrayOutputStream logBytes = new ByteArrayOutputStream();
|
||||||
})
|
try (var ignored = k8sclient.pods().resource(pod).watchLog(logBytes)) {
|
||||||
.collect(Collectors.toList());
|
Awaitility.await().atMost(1, TimeUnit.MINUTES).until(() -> logBytes.toString(StandardCharsets.UTF_8)
|
||||||
assertThat(podlogs).anyMatch(l -> l.contains("password authentication failed for user \"" + username + "\""));
|
.contains("password authentication failed for user \"" + username + "\""));
|
||||||
});
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
savePodLogs();
|
savePodLogs();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private StatefulSet getStatefulSet(Keycloak kc) {
|
||||||
|
return k8sclient.apps().statefulSets().withName(kc.getMetadata().getName()).require();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSecretsCanBeUnWatched() {
|
public void testSecretsCanBeUnWatched() {
|
||||||
try {
|
try {
|
||||||
var kc = getDefaultKeycloakDeployment();
|
var kc = getTestKeycloakDeployment(true);
|
||||||
deployKeycloak(k8sclient, kc, true);
|
deployKeycloak(k8sclient, kc, true);
|
||||||
|
|
||||||
Log.info("Updating KC to not to rely on DB Secret");
|
Log.info("Updating KC to not to rely on DB Secret");
|
||||||
|
@ -162,12 +155,12 @@ public class WatchedSecretsTest extends BaseOperatorTest {
|
||||||
@Test
|
@Test
|
||||||
public void testSingleSecretMultipleKeycloaks() {
|
public void testSingleSecretMultipleKeycloaks() {
|
||||||
try {
|
try {
|
||||||
var kc1 = getDefaultKeycloakDeployment();
|
var kc1 = getTestKeycloakDeployment(true);
|
||||||
var kc1Hostname = new HostnameSpecBuilder().withHostname("kc1.local").build();
|
var kc1Hostname = new HostnameSpecBuilder().withHostname("kc1.local").build();
|
||||||
kc1.getMetadata().setName(kc1.getMetadata().getName() + "-1");
|
kc1.getMetadata().setName(kc1.getMetadata().getName() + "-1");
|
||||||
kc1.getSpec().setHostnameSpec(kc1Hostname);
|
kc1.getSpec().setHostnameSpec(kc1Hostname);
|
||||||
|
|
||||||
var kc2 = getDefaultKeycloakDeployment();
|
var kc2 = getTestKeycloakDeployment(true);
|
||||||
var kc2Hostname = new HostnameSpecBuilder().withHostname("kc2.local").build();
|
var kc2Hostname = new HostnameSpecBuilder().withHostname("kc2.local").build();
|
||||||
kc2.getMetadata().setName(kc2.getMetadata().getName() + "-2");
|
kc2.getMetadata().setName(kc2.getMetadata().getName() + "-2");
|
||||||
kc2.getSpec().setHostnameSpec(kc2Hostname); // to prevent Ingress conflicts
|
kc2.getSpec().setHostnameSpec(kc2Hostname); // to prevent Ingress conflicts
|
||||||
|
@ -204,12 +197,14 @@ public class WatchedSecretsTest extends BaseOperatorTest {
|
||||||
private void testDeploymentRestarted(Set<Keycloak> crsToBeRestarted, Set<Keycloak> crsNotToBeRestarted, Runnable action) {
|
private void testDeploymentRestarted(Set<Keycloak> crsToBeRestarted, Set<Keycloak> crsNotToBeRestarted, Runnable action) {
|
||||||
boolean restartExpected = !crsToBeRestarted.isEmpty();
|
boolean restartExpected = !crsToBeRestarted.isEmpty();
|
||||||
|
|
||||||
List<String> podsToBeRestarted = getPodNamesForCrs(crsToBeRestarted);
|
var toBeRestarted = crsToBeRestarted.stream().collect(Collectors.toMap(Function.identity(), k -> getStatefulSet(k).getStatus().getUpdateRevision()));
|
||||||
List<String> podsNotToBeRestarted = getPodNamesForCrs(crsNotToBeRestarted);
|
var notToBeRestarted = crsNotToBeRestarted.stream().collect(Collectors.toMap(Function.identity(), k -> getStatefulSet(k).getStatus().getUpdateRevision()));
|
||||||
|
|
||||||
action.run();
|
action.run();
|
||||||
|
|
||||||
if (restartExpected) {
|
if (restartExpected) {
|
||||||
|
// this depends on the restart taking long enough to detect after the action is run
|
||||||
|
// we may want to switch to using an informer that runs before the action
|
||||||
assertRollingUpdate(crsToBeRestarted, true);
|
assertRollingUpdate(crsToBeRestarted, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,45 +215,33 @@ public class WatchedSecretsTest extends BaseOperatorTest {
|
||||||
if (restartExpected) {
|
if (restartExpected) {
|
||||||
Awaitility.await()
|
Awaitility.await()
|
||||||
.untilAsserted(() -> {
|
.untilAsserted(() -> {
|
||||||
List<String> newPods = getPodNamesForCrs(allCrs);
|
toBeRestarted.forEach((k, version) -> {
|
||||||
Log.infof("Pods to be restarted: %s\nPods NOT to be restarted: %s\nCurrent Pods: %s",
|
// make sure a new version was fully rolled in
|
||||||
podsToBeRestarted, podsNotToBeRestarted, newPods);
|
var status = getStatefulSet(k).getStatus();
|
||||||
assertThat(newPods).noneMatch(podsToBeRestarted::contains);
|
assertThat(status.getUpdateRevision()).isEqualTo(status.getCurrentRevision());
|
||||||
assertThat(newPods).containsAll(podsNotToBeRestarted);
|
assertThat(status.getUpdateRevision()).isNotEqualTo(version);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
if (!notToBeRestarted.isEmpty()) {
|
||||||
Awaitility.await()
|
Awaitility.await()
|
||||||
.during(10, TimeUnit.SECONDS) // to ensure no pods were created
|
.during(10, TimeUnit.SECONDS) // to ensure no pods were created
|
||||||
.untilAsserted(() -> {
|
.untilAsserted(() -> {
|
||||||
List<String> newPods = getPodNamesForCrs(allCrs);
|
notToBeRestarted.forEach((k, version) -> {
|
||||||
Log.infof("Pods NOT to be restarted: %s, expected pods: %s\nAsserting current pods are unchanged: %s",
|
// make sure the version has stayed the same
|
||||||
podsNotToBeRestarted, newPods);
|
var status = getStatefulSet(k).getStatus();
|
||||||
assertThat(newPods).isEqualTo(podsNotToBeRestarted);
|
assertThat(status.getUpdateRevision()).isEqualTo(status.getCurrentRevision());
|
||||||
|
assertThat(status.getUpdateRevision()).isEqualTo(version);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getPodNamesForCrs(Set<Keycloak> crs) {
|
|
||||||
return k8sclient
|
|
||||||
.pods()
|
|
||||||
.inNamespace(namespace)
|
|
||||||
.list()
|
|
||||||
.getItems()
|
|
||||||
.stream()
|
|
||||||
.map(pod -> pod.getMetadata().getName() + pod.getMetadata().getCreationTimestamp())
|
|
||||||
.filter(pod -> crs.stream().map(c -> c.getMetadata().getName()).anyMatch(pod::startsWith))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertRollingUpdate(Set<Keycloak> crs, boolean expectedStatus) {
|
private void assertRollingUpdate(Set<Keycloak> crs, boolean expectedStatus) {
|
||||||
Awaitility.await()
|
Awaitility.await()
|
||||||
.untilAsserted(() -> {
|
.untilAsserted(() -> {
|
||||||
for (var cr : crs) {
|
for (var cr : crs) {
|
||||||
Keycloak kc = k8sclient.resources(Keycloak.class)
|
Keycloak kc = k8sclient.resource(cr).get();
|
||||||
.inNamespace(namespace)
|
|
||||||
.withName(cr.getMetadata().getName())
|
|
||||||
.get();
|
|
||||||
assertKeycloakStatusCondition(kc, KeycloakStatusCondition.ROLLING_UPDATE, expectedStatus);
|
assertKeycloakStatusCondition(kc, KeycloakStatusCondition.ROLLING_UPDATE, expectedStatus);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.keycloak.operator.crds.v2alpha1.deployment.spec.HttpSpecBuilder;
|
||||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.UnsupportedSpec;
|
import org.keycloak.operator.crds.v2alpha1.deployment.spec.UnsupportedSpec;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
@ -93,7 +94,7 @@ public class PodTemplateTest {
|
||||||
|
|
||||||
var deployment = new KeycloakDeployment(null, config, kc, existingDeployment, "dummy-admin");
|
var deployment = new KeycloakDeployment(null, config, kc, existingDeployment, "dummy-admin");
|
||||||
|
|
||||||
return (StatefulSet) deployment.getReconciledResource().get();
|
return deployment.getReconciledResource().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private StatefulSet getDeployment(PodTemplateSpec podTemplate, StatefulSet existingDeployment) {
|
private StatefulSet getDeployment(PodTemplateSpec podTemplate, StatefulSet existingDeployment) {
|
||||||
|
@ -368,4 +369,16 @@ public class PodTemplateTest {
|
||||||
assertEquals("/some/health/ready", fourth.getReadinessProbe().getHttpGet().getPath());
|
assertEquals("/some/health/ready", fourth.getReadinessProbe().getHttpGet().getPath());
|
||||||
assertEquals("/some/health/live", fourth.getLivenessProbe().getHttpGet().getPath());
|
assertEquals("/some/health/live", fourth.getLivenessProbe().getHttpGet().getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultArgs() {
|
||||||
|
// Arrange
|
||||||
|
PodTemplateSpec additionalPodTemplate = null;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var podTemplate = getDeployment(additionalPodTemplate).getSpec().getTemplate();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(podTemplate.getSpec().getContainers().get(0).getArgs()).doesNotContain("--optimized");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,25 +19,29 @@ package org.keycloak.operator.testsuite.utils;
|
||||||
|
|
||||||
import io.fabric8.kubernetes.api.model.HasMetadata;
|
import io.fabric8.kubernetes.api.model.HasMetadata;
|
||||||
import io.fabric8.kubernetes.api.model.Pod;
|
import io.fabric8.kubernetes.api.model.Pod;
|
||||||
|
import io.fabric8.kubernetes.api.model.PodBuilder;
|
||||||
import io.fabric8.kubernetes.api.model.Secret;
|
import io.fabric8.kubernetes.api.model.Secret;
|
||||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||||
import io.fabric8.kubernetes.client.KubernetesClientException;
|
import io.fabric8.kubernetes.client.KubernetesClientException;
|
||||||
|
import io.fabric8.kubernetes.client.dsl.ExecWatch;
|
||||||
import io.fabric8.kubernetes.client.dsl.Resource;
|
import io.fabric8.kubernetes.client.dsl.Resource;
|
||||||
import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil;
|
|
||||||
import io.fabric8.kubernetes.client.utils.Serialization;
|
import io.fabric8.kubernetes.client.utils.Serialization;
|
||||||
import io.quarkus.logging.Log;
|
import io.quarkus.logging.Log;
|
||||||
|
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
||||||
|
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakSpecBuilder;
|
||||||
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusCondition;
|
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusCondition;
|
||||||
import org.keycloak.operator.testsuite.integration.BaseOperatorTest;
|
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||||
|
@ -48,16 +52,13 @@ public final class K8sUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Keycloak getDefaultKeycloakDeployment() {
|
public static Keycloak getDefaultKeycloakDeployment() {
|
||||||
Keycloak kc = getResourceFromFile("example-keycloak.yaml", Keycloak.class);
|
return getResourceFromFile("example-keycloak.yaml", Keycloak.class);
|
||||||
kc.getMetadata().setNamespace(BaseOperatorTest.getCurrentNamespace());
|
|
||||||
return kc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Secret getDefaultTlsSecret() {
|
public static Secret getDefaultTlsSecret() {
|
||||||
return getResourceFromFile("example-tls-secret.yaml", Secret.class);
|
return getResourceFromFile("example-tls-secret.yaml", Secret.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void deployKeycloak(KubernetesClient client, Keycloak kc, boolean waitUntilReady) {
|
public static void deployKeycloak(KubernetesClient client, Keycloak kc, boolean waitUntilReady) {
|
||||||
deployKeycloak(client, kc, waitUntilReady, true);
|
deployKeycloak(client, kc, waitUntilReady, true);
|
||||||
}
|
}
|
||||||
|
@ -83,6 +84,10 @@ public final class K8sUtils {
|
||||||
set(client, getDefaultTlsSecret());
|
set(client, getDefaultTlsSecret());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// speed the cleanup of pods
|
||||||
|
kc.setSpec(new KeycloakSpecBuilder(kc.getSpec()).editOrNewUnsupported().editOrNewPodTemplate().editOrNewSpec()
|
||||||
|
.withTerminationGracePeriodSeconds(0L).endSpec().endPodTemplate().endUnsupported().build());
|
||||||
|
|
||||||
set(client, kc);
|
set(client, kc);
|
||||||
|
|
||||||
if (waitUntilReady) {
|
if (waitUntilReady) {
|
||||||
|
@ -90,10 +95,6 @@ public final class K8sUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deployDefaultKeycloak(KubernetesClient client) {
|
|
||||||
deployKeycloak(client, getDefaultKeycloakDeployment(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void waitForKeycloakToBeReady(KubernetesClient client, Keycloak kc) {
|
public static void waitForKeycloakToBeReady(KubernetesClient client, Keycloak kc) {
|
||||||
Log.infof("Waiting for Keycloak \"%s\"", kc.getMetadata().getName());
|
Log.infof("Waiting for Keycloak \"%s\"", kc.getMetadata().getName());
|
||||||
Awaitility.await()
|
Awaitility.await()
|
||||||
|
@ -113,37 +114,37 @@ public final class K8sUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String inClusterCurl(KubernetesClient k8sclient, String namespace, String... args) {
|
public static String inClusterCurl(KubernetesClient k8sclient, String namespace, String... args) {
|
||||||
var podName = KubernetesResourceUtil.sanitizeName("curl-" + UUID.randomUUID());
|
var podName = "curl-pod";
|
||||||
try {
|
try {
|
||||||
Pod curlPod = k8sclient.run().inNamespace(namespace)
|
Pod curlPod = new PodBuilder().withNewMetadata().withName(podName).endMetadata().withNewSpec()
|
||||||
.withNewRunConfig()
|
.addNewContainer()
|
||||||
.withArgs(args)
|
.withImage("curlimages/curl:8.1.2")
|
||||||
.withName(podName)
|
.withCommand("sh")
|
||||||
.withImage("curlimages/curl:7.78.0")
|
.withName("curl")
|
||||||
.withRestartPolicy("Never")
|
.withStdin()
|
||||||
.done();
|
.endContainer()
|
||||||
Log.info("Waiting for curl Pod to finish running");
|
.endSpec()
|
||||||
Awaitility.await().atMost(3, TimeUnit.MINUTES)
|
.build();
|
||||||
.until(() -> {
|
|
||||||
String phase =
|
|
||||||
k8sclient.pods().inNamespace(namespace).withName(podName).get()
|
|
||||||
.getStatus().getPhase();
|
|
||||||
return phase.equals("Succeeded") || phase.equals("Failed");
|
|
||||||
});
|
|
||||||
|
|
||||||
String curlOutput =
|
try {
|
||||||
k8sclient.pods().inNamespace(namespace)
|
k8sclient.resource(curlPod).create();
|
||||||
.withName(curlPod.getMetadata().getName()).getLog();
|
} catch (KubernetesClientException e) {
|
||||||
|
if (e.getCode() != HttpURLConnection.HTTP_CONFLICT) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return curlOutput;
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||||
} catch (KubernetesClientException ex) {
|
|
||||||
throw new AssertionError(ex);
|
try (ExecWatch watch = k8sclient.pods().resource(curlPod).withReadyWaitTimeout(60000)
|
||||||
} finally {
|
.writingOutput(output)
|
||||||
Log.info("Deleting curl Pod");
|
.exec(Stream.concat(Stream.of("curl"), Stream.of(args)).toArray(String[]::new))) {
|
||||||
k8sclient.pods().inNamespace(namespace).withName(podName).delete();
|
watch.exitCode().get(15, TimeUnit.SECONDS);
|
||||||
Awaitility.await().atMost(2, TimeUnit.MINUTES)
|
}
|
||||||
.until(() -> k8sclient.pods().inNamespace(namespace).withName(podName)
|
|
||||||
.get() == null);
|
return output.toString(StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw KubernetesClientException.launderThrowable(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue