fix: check operand StatefulSet is ready before creating job (#24720)
closes #24526 Signed-off-by: Steve Hawkins <shawkins@redhat.com> Co-authored-by: Piotr Godowski <Piotr.Godowski@pl.ibm.com>
This commit is contained in:
parent
190dade456
commit
b6d5083e9b
2 changed files with 45 additions and 1 deletions
|
@ -17,6 +17,7 @@
|
||||||
package org.keycloak.operator.controllers;
|
package org.keycloak.operator.controllers;
|
||||||
|
|
||||||
import io.fabric8.kubernetes.api.model.apps.StatefulSet;
|
import io.fabric8.kubernetes.api.model.apps.StatefulSet;
|
||||||
|
import io.fabric8.kubernetes.api.model.apps.StatefulSetStatus;
|
||||||
import io.fabric8.kubernetes.api.model.batch.v1.Job;
|
import io.fabric8.kubernetes.api.model.batch.v1.Job;
|
||||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||||
import io.javaoperatorsdk.operator.api.reconciler.Context;
|
import io.javaoperatorsdk.operator.api.reconciler.Context;
|
||||||
|
@ -37,6 +38,7 @@ import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImportStatus
|
||||||
import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImportStatusCondition;
|
import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImportStatusCondition;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
@ -74,7 +76,9 @@ public class KeycloakRealmImportController implements Reconciler<KeycloakRealmIm
|
||||||
|
|
||||||
if (existingDeployment != null) {
|
if (existingDeployment != null) {
|
||||||
context.managedDependentResourceContext().put(StatefulSet.class, existingDeployment);
|
context.managedDependentResourceContext().put(StatefulSet.class, existingDeployment);
|
||||||
jobDependentResource.reconcile(realm, context);
|
if (getReadyReplicas(existingDeployment) > 0) {
|
||||||
|
jobDependentResource.reconcile(realm, context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateStatus(statusBuilder, realm, existingJob, existingDeployment);
|
updateStatus(statusBuilder, realm, existingJob, existingDeployment);
|
||||||
|
@ -118,6 +122,11 @@ public class KeycloakRealmImportController implements Reconciler<KeycloakRealmIm
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getReadyReplicas(existingDeployment) < 1) {
|
||||||
|
status.addErrorMessage("Deployment not yet ready, waiting for it to be ready");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (existingJob == null) {
|
if (existingJob == null) {
|
||||||
Log.info("Job about to start");
|
Log.info("Job about to start");
|
||||||
status.addStartedMessage("Import Job will start soon");
|
status.addStartedMessage("Import Job will start soon");
|
||||||
|
@ -145,6 +154,10 @@ public class KeycloakRealmImportController implements Reconciler<KeycloakRealmIm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Integer getReadyReplicas(StatefulSet existingDeployment) {
|
||||||
|
return Optional.ofNullable(existingDeployment.getStatus()).map(StatefulSetStatus::getReadyReplicas).orElse(0);
|
||||||
|
}
|
||||||
|
|
||||||
private void rollingRestart(KeycloakRealmImport realmCR) {
|
private void rollingRestart(KeycloakRealmImport realmCR) {
|
||||||
client.apps().statefulSets()
|
client.apps().statefulSets()
|
||||||
.inNamespace(realmCR.getMetadata().getNamespace())
|
.inNamespace(realmCR.getMetadata().getNamespace())
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestInfo;
|
import org.junit.jupiter.api.TestInfo;
|
||||||
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
|
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
|
||||||
import org.keycloak.operator.controllers.KeycloakServiceDependentResource;
|
import org.keycloak.operator.controllers.KeycloakServiceDependentResource;
|
||||||
|
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
|
||||||
import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImport;
|
import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImport;
|
||||||
import org.keycloak.operator.testsuite.utils.CRAssert;
|
import org.keycloak.operator.testsuite.utils.CRAssert;
|
||||||
import org.keycloak.operator.testsuite.utils.K8sUtils;
|
import org.keycloak.operator.testsuite.utils.K8sUtils;
|
||||||
|
@ -195,4 +196,34 @@ public class RealmImportTest extends BaseOperatorTest {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFailedRealmImportWhenKeycloakNotAvailable() {
|
||||||
|
// Arrange
|
||||||
|
Keycloak kc = getTestKeycloakDeployment(true);
|
||||||
|
kc.getSpec().setInstances(0);
|
||||||
|
|
||||||
|
// don't wait for Keycloak being available, since it has no instances
|
||||||
|
deployKeycloak(k8sclient, kc, false);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
K8sUtils.set(k8sclient, getClass().getResourceAsStream("/example-realm.yaml"));
|
||||||
|
|
||||||
|
var crSelector = k8sclient
|
||||||
|
.resources(KeycloakRealmImport.class)
|
||||||
|
.inNamespace(namespace)
|
||||||
|
.withName("example-count0-kc");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Awaitility.await()
|
||||||
|
.atMost(3, MINUTES)
|
||||||
|
.pollDelay(5, SECONDS)
|
||||||
|
.ignoreExceptions()
|
||||||
|
.untilAsserted(() -> {
|
||||||
|
KeycloakRealmImport keycloak = crSelector.get();
|
||||||
|
CRAssert.assertKeycloakRealmImportStatusCondition(keycloak, DONE, false);
|
||||||
|
CRAssert.assertKeycloakRealmImportStatusCondition(keycloak, STARTED, false);
|
||||||
|
CRAssert.assertKeycloakRealmImportStatusCondition(keycloak, HAS_ERRORS, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue