Respect http-relative-path with probes

This commit is contained in:
andreaTP 2022-05-30 11:49:01 +01:00 committed by Bruno Oliveira da Silva
parent c31d37ddf1
commit f9c6ea84ad
4 changed files with 126 additions and 21 deletions

View file

@ -51,4 +51,6 @@ public final class Constants {
public static final String INSECURE_DISABLE = "INSECURE-DISABLE";
public static final String CERTIFICATES_FOLDER = "/mnt/certificates";
public static String KEYCLOAK_HTTP_RELATIVE_PATH_KEY = "http-relative-path";
}

View file

@ -20,6 +20,7 @@ import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
import io.fabric8.kubernetes.api.model.EnvVarSourceBuilder;
import io.fabric8.kubernetes.api.model.ExecActionBuilder;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.PodTemplateSpec;
import io.fabric8.kubernetes.api.model.ResourceRequirements;
@ -36,7 +37,9 @@ import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusBuilder;
import org.keycloak.operator.crds.v2alpha1.deployment.ValueOrSecret;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -347,6 +350,7 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu
var kcContainer = deployment.getSpec().getTemplate().getSpec().getContainers().get(0);
var tlsSecret = this.keycloakCR.getSpec().getTlsSecret();
var envVars = kcContainer.getEnv();
if (this.keycloakCR.getSpec().isHttp()) {
var disableTls = List.of(
new EnvVarBuilder()
@ -363,11 +367,6 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu
.build());
envVars.addAll(disableTls);
kcContainer.getReadinessProbe().getExec().setCommand(
List.of("curl", "--head", "--fail", "--silent", "http://127.0.0.1:" + Constants.KEYCLOAK_HTTP_PORT + "/health/ready"));
kcContainer.getLivenessProbe().getExec().setCommand(
List.of("curl", "--head", "--fail", "--silent", "http://127.0.0.1:" + Constants.KEYCLOAK_HTTP_PORT + "/health/live"));
} else {
var enabledTls = List.of(
new EnvVarBuilder()
@ -401,6 +400,66 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu
deployment.getSpec().getTemplate().getSpec().getVolumes().add(volume);
kcContainer.getVolumeMounts().add(volumeMount);
}
var userRelativePath = readConfigurationValue(Constants.KEYCLOAK_HTTP_RELATIVE_PATH_KEY);
var kcRelativePath = (userRelativePath == null) ? "/" : userRelativePath;
var protocol = (this.keycloakCR.getSpec().isHttp()) ? "http" : "https";
var kcPort = (this.keycloakCR.getSpec().isHttp()) ? Constants.KEYCLOAK_HTTP_PORT : Constants.KEYCLOAK_HTTPS_PORT;
var baseProbe = new ArrayList<>(List.of("curl", "--head", "--fail", "--silent"));
if (!this.keycloakCR.getSpec().isHttp()) {
baseProbe.add("--insecure");
}
var readyProbe = new ArrayList<>(baseProbe);
readyProbe.add(protocol + "://127.0.0.1:" + kcPort + kcRelativePath + "/health/ready");
var liveProbe = new ArrayList<>(baseProbe);
liveProbe.add(protocol + "://127.0.0.1:" + kcPort + kcRelativePath + "/health/live");
kcContainer
.getReadinessProbe()
.setExec(new ExecActionBuilder().withCommand(readyProbe).build());
kcContainer
.getLivenessProbe()
.setExec(new ExecActionBuilder().withCommand(liveProbe).build());
}
public String readConfigurationValue(String key) {
if (this.keycloakCR != null &&
this.keycloakCR.getSpec() != null &&
this.keycloakCR.getSpec().getServerConfiguration() != null
) {
var serverConfigValue = this.keycloakCR
.getSpec()
.getServerConfiguration()
.stream()
.filter(sc -> sc.getName().equals(key))
.findFirst();
if (serverConfigValue.isPresent()) {
if (serverConfigValue.get().getValue() != null) {
return serverConfigValue.get().getValue();
} else {
var secretSelector = serverConfigValue.get().getSecret();
if (secretSelector == null) {
throw new IllegalStateException("Secret " + serverConfigValue.get().getName() + " not defined");
}
var secret = client.secrets().inNamespace(getNamespace()).withName(secretSelector.getName()).get();
if (secret == null) {
throw new IllegalStateException("Secret " + secretSelector.getName() + " not found in cluster");
}
if (secret.getData().containsKey(secretSelector.getKey())) {
return new String(Base64.getDecoder().decode(secret.getData().get(secretSelector.getKey())), StandardCharsets.UTF_8);
} else {
throw new IllegalStateException("Secret " + secretSelector.getName() + " doesn't contain the expected key " + secretSelector.getKey());
}
}
} else {
return null;
}
} else {
return null;
}
}
private Deployment createBaseDeployment() {

View file

@ -27,26 +27,10 @@ spec:
- containerPort: 8080
protocol: TCP
livenessProbe:
exec:
command:
- curl
- --insecure
- --head
- --fail
- --silent
- https://127.0.0.1:8443/health/live
initialDelaySeconds: 20
periodSeconds: 2
failureThreshold: 150
readinessProbe:
exec:
command:
- curl
- --insecure
- --head
- --fail
- --silent
- https://127.0.0.1:8443/health/ready
initialDelaySeconds: 20
periodSeconds: 2
failureThreshold: 250

View file

@ -1,6 +1,8 @@
package org.keycloak.operator;
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
import io.fabric8.kubernetes.api.model.SecretBuilder;
import io.fabric8.kubernetes.api.model.SecretKeySelectorBuilder;
import io.fabric8.kubernetes.api.model.apps.DeploymentSpecBuilder;
import io.quarkus.logging.Log;
import io.quarkus.test.junit.QuarkusTest;
@ -20,6 +22,7 @@ import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
@ -354,4 +357,61 @@ public class KeycloakDeploymentE2EIT extends ClusterOperatorTest {
}
}
@Test
public void testHttpRelativePathWithPlainValue() {
try {
var kc = getDefaultKeycloakDeployment();
kc.getSpec().getServerConfiguration().add(new ValueOrSecret(Constants.KEYCLOAK_HTTP_RELATIVE_PATH_KEY, "/foobar"));
deployKeycloak(k8sclient, kc, true);
var pods = k8sclient
.pods()
.inNamespace(namespace)
.withLabels(Constants.DEFAULT_LABELS)
.list()
.getItems();
assertTrue(pods.get(0).getSpec().getContainers().get(0).getReadinessProbe().getExec().getCommand().stream().collect(Collectors.joining()).contains("foobar"));
} catch (Exception e) {
savePodLogs();
throw e;
}
}
@Test
public void testHttpRelativePathWithSecretValue() {
try {
var kc = getDefaultKeycloakDeployment();
var secretName = "my-http-relative-path";
var keyName = "rel-path";
var httpRelativePathSecret = new SecretBuilder()
.withNewMetadata()
.withName(secretName)
.withNamespace(namespace)
.endMetadata()
.addToStringData(keyName, "/barfoo")
.build();
k8sclient.secrets().inNamespace(namespace).createOrReplace(httpRelativePathSecret);
kc.getSpec().getServerConfiguration().add(new ValueOrSecret(Constants.KEYCLOAK_HTTP_RELATIVE_PATH_KEY,
new SecretKeySelectorBuilder()
.withName(secretName)
.withKey(keyName)
.build()));
deployKeycloak(k8sclient, kc, true);
var pods = k8sclient
.pods()
.inNamespace(namespace)
.withLabels(Constants.DEFAULT_LABELS)
.list()
.getItems();
assertTrue(pods.get(0).getSpec().getContainers().get(0).getReadinessProbe().getExec().getCommand().stream().collect(Collectors.joining()).contains("barfoo"));
} catch (Exception e) {
savePodLogs();
throw e;
}
}
}