diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakIngress.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakIngress.java index 31b394dde5..c2c62b7cb8 100644 --- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakIngress.java +++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakIngress.java @@ -45,7 +45,7 @@ public class KeycloakIngress extends OperatorManagedResource implements StatusUp protected Optional getReconciledResource() { IngressSpec ingressSpec = keycloak.getSpec().getIngressSpec(); if (ingressSpec != null && !ingressSpec.isIngressEnabled()) { - if (existingIngress != null) { + if (existingIngress != null && isExistingIngressFromSameOwnerReference()) { deleteExistingIngress(); } return Optional.empty(); @@ -114,6 +114,16 @@ public class KeycloakIngress extends OperatorManagedResource implements StatusUp 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() { return client .network() diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakIngressTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakIngressTest.java index 328e922566..6915cd7f8b 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakIngressTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakIngressTest.java @@ -17,13 +17,17 @@ 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.client.dsl.Resource; import io.quarkus.logging.Log; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; 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.HostnameSpecBuilder; import org.keycloak.operator.testsuite.utils.K8sUtils; @@ -31,6 +35,7 @@ import org.keycloak.operator.controllers.KeycloakIngress; import java.util.Map; +import static java.util.concurrent.TimeUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThat; 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); }); } + + @Test + public void testCustomIngressDeletion() { + + Keycloak defaultKeycloakDeployment = K8sUtils.getDefaultKeycloakDeployment(); + String kcDeploymentName = defaultKeycloakDeployment.getMetadata().getName(); + Resource 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; + } + } diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/unit/IngressLogicTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/unit/IngressLogicTest.java index 6bf473a0b6..8a691d7fbf 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/unit/IngressLogicTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/unit/IngressLogicTest.java @@ -17,9 +17,13 @@ package org.keycloak.operator.testsuite.unit; +import java.util.Collections; 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.keycloak.operator.Constants; import org.keycloak.operator.controllers.KeycloakIngress; import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak; 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) { var kc = K8sUtils.getDefaultKeycloakDeployment(); + kc.getMetadata().setUid("this-is-a-fake-uid"); if (ingressSpecDefined) { kc.getSpec().setIngressSpec(new IngressSpec()); if (defaultIngressEnabled != null) kc.getSpec().getIngressSpec().setIngressEnabled(defaultIngressEnabled); @@ -80,7 +85,23 @@ public class IngressLogicTest { @Override protected Ingress fetchExistingIngress() { 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 { return null; }