Make sure the customized Ingress resource isn't deleted when the Keycloak deployed by Operator is killed.
Closes #14433
This commit is contained in:
parent
a64f6dcfc2
commit
cb78ea06b0
3 changed files with 115 additions and 2 deletions
|
@ -45,7 +45,7 @@ public class KeycloakIngress extends OperatorManagedResource implements StatusUp
|
||||||
protected Optional<HasMetadata> getReconciledResource() {
|
protected Optional<HasMetadata> getReconciledResource() {
|
||||||
IngressSpec ingressSpec = keycloak.getSpec().getIngressSpec();
|
IngressSpec ingressSpec = keycloak.getSpec().getIngressSpec();
|
||||||
if (ingressSpec != null && !ingressSpec.isIngressEnabled()) {
|
if (ingressSpec != null && !ingressSpec.isIngressEnabled()) {
|
||||||
if (existingIngress != null) {
|
if (existingIngress != null && isExistingIngressFromSameOwnerReference()) {
|
||||||
deleteExistingIngress();
|
deleteExistingIngress();
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
|
@ -114,6 +114,16 @@ public class KeycloakIngress extends OperatorManagedResource implements StatusUp
|
||||||
client.network().v1().ingresses().inNamespace(getNamespace()).delete(existingIngress);
|
client.network().v1().ingresses().inNamespace(getNamespace()).delete(existingIngress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isExistingIngressFromSameOwnerReference() {
|
||||||
|
|
||||||
|
return existingIngress
|
||||||
|
.getMetadata()
|
||||||
|
.getOwnerReferences()
|
||||||
|
.stream()
|
||||||
|
.anyMatch(oneOwnerRef -> oneOwnerRef.getUid().equalsIgnoreCase(keycloak.getMetadata().getUid()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
protected Ingress fetchExistingIngress() {
|
protected Ingress fetchExistingIngress() {
|
||||||
return client
|
return client
|
||||||
.network()
|
.network()
|
||||||
|
|
|
@ -17,13 +17,17 @@
|
||||||
|
|
||||||
package org.keycloak.operator.testsuite.integration;
|
package org.keycloak.operator.testsuite.integration;
|
||||||
|
|
||||||
|
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
|
||||||
|
import io.fabric8.kubernetes.api.model.networking.v1.IngressBuilder;
|
||||||
import io.fabric8.kubernetes.api.model.networking.v1.ServiceBackendPortBuilder;
|
import io.fabric8.kubernetes.api.model.networking.v1.ServiceBackendPortBuilder;
|
||||||
|
import io.fabric8.kubernetes.client.dsl.Resource;
|
||||||
import io.quarkus.logging.Log;
|
import io.quarkus.logging.Log;
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
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.spec.IngressSpec;
|
import org.keycloak.operator.crds.v2alpha1.deployment.spec.IngressSpec;
|
||||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.HostnameSpecBuilder;
|
import org.keycloak.operator.crds.v2alpha1.deployment.spec.HostnameSpecBuilder;
|
||||||
import org.keycloak.operator.testsuite.utils.K8sUtils;
|
import org.keycloak.operator.testsuite.utils.K8sUtils;
|
||||||
|
@ -31,6 +35,7 @@ import org.keycloak.operator.controllers.KeycloakIngress;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
@ -188,4 +193,81 @@ public class KeycloakIngressTest extends BaseOperatorTest {
|
||||||
assertThat(k8sclient.network().v1().ingresses().inNamespace(namespace).list().getItems().size()).isEqualTo(0);
|
assertThat(k8sclient.network().v1().ingresses().inNamespace(namespace).list().getItems().size()).isEqualTo(0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCustomIngressDeletion() {
|
||||||
|
|
||||||
|
Keycloak defaultKeycloakDeployment = K8sUtils.getDefaultKeycloakDeployment();
|
||||||
|
String kcDeploymentName = defaultKeycloakDeployment.getMetadata().getName();
|
||||||
|
Resource<Ingress> customIngressDeployedManuallySelector = null;
|
||||||
|
Ingress customIngressCreatedManually;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
customIngressCreatedManually = createCustomIngress(kcDeploymentName, namespace, 8443);
|
||||||
|
|
||||||
|
customIngressDeployedManuallySelector = k8sclient
|
||||||
|
.network()
|
||||||
|
.v1()
|
||||||
|
.ingresses()
|
||||||
|
.inNamespace(namespace)
|
||||||
|
.withName(customIngressCreatedManually.getMetadata().getName());
|
||||||
|
|
||||||
|
Awaitility.await().atMost(1, MINUTES).untilAsserted(() -> {
|
||||||
|
assertThat(k8sclient.network().v1().ingresses().inNamespace(namespace).list().getItems().size()).isEqualTo(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
Log.info("Redeploying the Keycloak CR with default Ingress disabled");
|
||||||
|
defaultKeycloakDeployment.getSpec().setIngressSpec(new IngressSpec());
|
||||||
|
defaultKeycloakDeployment.getSpec().getIngressSpec().setIngressEnabled(false);
|
||||||
|
|
||||||
|
K8sUtils.deployKeycloak(k8sclient, defaultKeycloakDeployment, true);
|
||||||
|
|
||||||
|
Awaitility.await().untilAsserted(() -> {
|
||||||
|
Log.info("Make sure the Custom Ingress still remains");
|
||||||
|
assertThat(k8sclient.network().v1().ingresses().inNamespace(namespace).list().getItems().size()).isEqualTo(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
savePodLogs();
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
Log.info("Destroying the Custom Ingress created manually to avoid errors in others Tests methods");
|
||||||
|
if (customIngressDeployedManuallySelector != null && customIngressDeployedManuallySelector.isReady()) {
|
||||||
|
assertThat(customIngressDeployedManuallySelector.delete()).isTrue();
|
||||||
|
Awaitility.await().untilAsserted(() -> {
|
||||||
|
assertThat(k8sclient.network().v1().ingresses().inNamespace(namespace).list().getItems().size()).isEqualTo(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Ingress createCustomIngress(String baseResourceName, String targetNamespace, int portNumber) {
|
||||||
|
|
||||||
|
Ingress customIngressCreated;
|
||||||
|
|
||||||
|
customIngressCreated = new IngressBuilder()
|
||||||
|
.withNewMetadata()
|
||||||
|
.withName(baseResourceName + Constants.KEYCLOAK_INGRESS_SUFFIX)
|
||||||
|
.withNamespace(targetNamespace)
|
||||||
|
.addToAnnotations("nginx.ingress.kubernetes.io/backend-protocol", "HTTPS")
|
||||||
|
.addToAnnotations("route.openshift.io/termination", "passthrough")
|
||||||
|
.endMetadata()
|
||||||
|
.withNewSpec()
|
||||||
|
.withNewDefaultBackend()
|
||||||
|
.withNewService()
|
||||||
|
.withName(baseResourceName + Constants.KEYCLOAK_SERVICE_SUFFIX)
|
||||||
|
.withNewPort()
|
||||||
|
.withNumber(portNumber)
|
||||||
|
.endPort()
|
||||||
|
.endService()
|
||||||
|
.endDefaultBackend()
|
||||||
|
.endSpec()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
customIngressCreated = k8sclient.network().v1().ingresses().inNamespace(targetNamespace).create(customIngressCreated);
|
||||||
|
|
||||||
|
return customIngressCreated;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,13 @@
|
||||||
|
|
||||||
package org.keycloak.operator.testsuite.unit;
|
package org.keycloak.operator.testsuite.unit;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import io.fabric8.kubernetes.api.model.OwnerReference;
|
||||||
|
import io.fabric8.kubernetes.api.model.OwnerReferenceBuilder;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.keycloak.operator.Constants;
|
||||||
import org.keycloak.operator.controllers.KeycloakIngress;
|
import org.keycloak.operator.controllers.KeycloakIngress;
|
||||||
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
||||||
import org.keycloak.operator.crds.v2alpha1.deployment.spec.IngressSpec;
|
import org.keycloak.operator.crds.v2alpha1.deployment.spec.IngressSpec;
|
||||||
|
@ -39,6 +43,7 @@ public class IngressLogicTest {
|
||||||
|
|
||||||
private static Keycloak getKeycloak(Boolean defaultIngressEnabled, boolean ingressSpecDefined, boolean tlsConfigured) {
|
private static Keycloak getKeycloak(Boolean defaultIngressEnabled, boolean ingressSpecDefined, boolean tlsConfigured) {
|
||||||
var kc = K8sUtils.getDefaultKeycloakDeployment();
|
var kc = K8sUtils.getDefaultKeycloakDeployment();
|
||||||
|
kc.getMetadata().setUid("this-is-a-fake-uid");
|
||||||
if (ingressSpecDefined) {
|
if (ingressSpecDefined) {
|
||||||
kc.getSpec().setIngressSpec(new IngressSpec());
|
kc.getSpec().setIngressSpec(new IngressSpec());
|
||||||
if (defaultIngressEnabled != null) kc.getSpec().getIngressSpec().setIngressEnabled(defaultIngressEnabled);
|
if (defaultIngressEnabled != null) kc.getSpec().getIngressSpec().setIngressEnabled(defaultIngressEnabled);
|
||||||
|
@ -80,7 +85,23 @@ public class IngressLogicTest {
|
||||||
@Override
|
@Override
|
||||||
protected Ingress fetchExistingIngress() {
|
protected Ingress fetchExistingIngress() {
|
||||||
if (ingressExists) {
|
if (ingressExists) {
|
||||||
return new IngressBuilder().withNewMetadata().endMetadata().build();
|
|
||||||
|
OwnerReference sameCROwnerRef = new OwnerReferenceBuilder()
|
||||||
|
.withApiVersion(cr.getApiVersion())
|
||||||
|
.withKind(cr.getKind())
|
||||||
|
.withName(cr.getMetadata().getName())
|
||||||
|
.withUid(cr.getMetadata().getUid())
|
||||||
|
.withBlockOwnerDeletion(true)
|
||||||
|
.withController(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return new IngressBuilder()
|
||||||
|
.withNewMetadata()
|
||||||
|
.withName(getName())
|
||||||
|
.withNamespace(cr.getMetadata().getNamespace())
|
||||||
|
.withOwnerReferences(Collections.singletonList(sameCROwnerRef))
|
||||||
|
.endMetadata()
|
||||||
|
.build();
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue