From 764ca50fc4a32bd3942ee3e675a1d41f3cb0ed86 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?V=C3=A1clav=20Muzik=C3=A1=C5=99?=
Date: Mon, 17 Feb 2025 16:30:05 +0100
Subject: [PATCH] Upgrade to Quarkus 3.18.2 (#37300)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Upgrade to Quarkus 3.18.2
Closes #37056
Signed-off-by: Václav Muzikář
* Update docs/documentation/upgrading/topics/changes/changes-26_2_0.adoc
Co-authored-by: Alexander Schwartz
Signed-off-by: Václav Muzikář
---------
Signed-off-by: Václav Muzikář
Signed-off-by: Václav Muzikář
Co-authored-by: Alexander Schwartz
---
.../tests/src/test/resources/ignored-links | 3 +-
.../topics/changes/changes-26_2_0.adoc | 15 ++++
.../upgrading/topics/changes/changes.adoc | 4 +
.../org/keycloak/operator/ContextUtils.java | 64 ++++++++++----
.../java/org/keycloak/operator/Utils.java | 7 --
.../KeycloakAdminSecretDependentResource.java | 23 +++--
.../controllers/KeycloakController.java | 84 ++++++++-----------
.../KeycloakDeploymentDependentResource.java | 53 +++++++-----
...loakDiscoveryServiceDependentResource.java | 15 +---
.../KeycloakIngressDependentResource.java | 5 +-
...eycloakNetworkPolicyDependentResource.java | 5 +-
.../KeycloakRealmImportController.java | 48 ++++-------
...ycloakRealmImportJobDependentResource.java | 21 ++---
...oakRealmImportSecretDependentResource.java | 7 +-
.../KeycloakServiceDependentResource.java | 13 +--
.../KeycloakUpdateJobDependentResource.java | 19 +++--
.../v2alpha1/deployment/KeycloakStatus.java | 5 +-
.../deployment/KeycloakStatusAggregator.java | 6 +-
.../operator/upgrade/UpgradeLogicFactory.java | 12 +--
.../upgrade/impl/AutoUpgradeLogic.java | 5 +-
.../upgrade/impl/BaseUpgradeLogic.java | 11 +--
.../impl/ForceRecreateUpgradeLogic.java | 5 +-
.../RecreateOnImageChangeUpgradeLogic.java | 7 +-
.../integration/BaseOperatorTest.java | 26 ++----
.../unit/KeycloakControllerTest.java | 12 +--
.../testsuite/unit/PodTemplateTest.java | 25 +++---
.../operator/testsuite/utils/CRAssert.java | 1 +
pom.xml | 6 +-
.../it/cli/dist/ProxyHostnameV2DistTest.java | 8 +-
.../keycloak/testframework/LogHandler.java | 10 ++-
.../federation/UserPropertyFileStorage.java | 3 +-
31 files changed, 273 insertions(+), 255 deletions(-)
create mode 100644 docs/documentation/upgrading/topics/changes/changes-26_2_0.adoc
diff --git a/docs/documentation/tests/src/test/resources/ignored-links b/docs/documentation/tests/src/test/resources/ignored-links
index 48f76390110..ce82ee3d13b 100644
--- a/docs/documentation/tests/src/test/resources/ignored-links
+++ b/docs/documentation/tests/src/test/resources/ignored-links
@@ -42,4 +42,5 @@ https://saml.xml.org*
# To be removed once KC 26.1 has been released
https://www.keycloak.org/server/logging#_configuring_levels_as_individual_options
https://www.keycloak.org/observability/*
-https://www.keycloak.org/high-availability/concepts-memory-and-cpu-sizing#_measuring_the_activity_of_a_running_keycloak_instance
\ No newline at end of file
+https://www.keycloak.org/high-availability/concepts-memory-and-cpu-sizing#_measuring_the_activity_of_a_running_keycloak_instance
+http://example.com:8080
\ No newline at end of file
diff --git a/docs/documentation/upgrading/topics/changes/changes-26_2_0.adoc b/docs/documentation/upgrading/topics/changes/changes-26_2_0.adoc
new file mode 100644
index 00000000000..98d0f96aad3
--- /dev/null
+++ b/docs/documentation/upgrading/topics/changes/changes-26_2_0.adoc
@@ -0,0 +1,15 @@
+== Breaking changes
+
+Breaking changes are identified as requiring changes from existing users to their configurations.
+
+=== Changes to port behaviour with the `X-Forwarded-Host` header
+
+The `X-Forwarded-Host` header can optionally also contain the port. In previous versions when the port was omitted from the header,
+{project_name} fell back to the actual request port. For example if {project_name} was listening on port 8080 and the request contained
+`X-Forwarded-Host: example.com` header, the resolved URL was `+http://example.com:8080+`.
+
+This is now changed and omitting the port results in removing it from the resolved URL. The resolved URL from the previous example
+would now be `http://example.com`.
+
+To mitigate that, either make your reverse proxy include the port in the `X-Forwarded-Host` header or configure it to set
+the `X-Forwarded-Port` header with the desired port.
diff --git a/docs/documentation/upgrading/topics/changes/changes.adoc b/docs/documentation/upgrading/topics/changes/changes.adoc
index ad9d6b90058..d659bfdc194 100644
--- a/docs/documentation/upgrading/topics/changes/changes.adoc
+++ b/docs/documentation/upgrading/topics/changes/changes.adoc
@@ -1,6 +1,10 @@
[[migration-changes]]
== Migration Changes
+=== Migrating to 26.2.0
+
+include::changes-26_2_0.adoc[leveloffset=2]
+
=== Migrating to 26.1.3
include::changes-26_1_3.adoc[leveloffset=2]
diff --git a/operator/src/main/java/org/keycloak/operator/ContextUtils.java b/operator/src/main/java/org/keycloak/operator/ContextUtils.java
index d7bb42f5fcb..de017c19f0d 100644
--- a/operator/src/main/java/org/keycloak/operator/ContextUtils.java
+++ b/operator/src/main/java/org/keycloak/operator/ContextUtils.java
@@ -17,44 +17,72 @@
package org.keycloak.operator;
-import java.util.Optional;
-
import io.fabric8.kubernetes.api.model.apps.StatefulSet;
import io.javaoperatorsdk.operator.api.reconciler.Context;
-import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
+import org.keycloak.operator.controllers.KeycloakDistConfigurator;
+import org.keycloak.operator.controllers.WatchedResources;
import org.keycloak.operator.upgrade.UpgradeType;
+import java.util.Optional;
+
public final class ContextUtils {
// context keys
- private static final String OLD_DEPLOYMENT_KEY = "current_stateful_set";
- private static final String NEW_DEPLOYMENT_KEY = "desired_new_stateful_set";
- private static final String UPGRADE_TYPE_KEY = "upgrade_type";
+ public static final String OLD_DEPLOYMENT_KEY = "current_stateful_set";
+ public static final String NEW_DEPLOYMENT_KEY = "desired_new_stateful_set";
+ public static final String UPGRADE_TYPE_KEY = "upgrade_type";
+ public static final String OPERATOR_CONFIG_KEY = "operator_config";
+ public static final String WATCHED_RESOURCES_KEY = "watched_resources";
+ public static final String DIST_CONFIGURATOR_KEY = "dist_configurator";
private ContextUtils() {}
- public static void storeCurrentStatefulSet(Context context, StatefulSet statefulSet) {
- context.managedDependentResourceContext().put(OLD_DEPLOYMENT_KEY, statefulSet);
+ public static void storeCurrentStatefulSet(Context> context, StatefulSet statefulSet) {
+ context.managedWorkflowAndDependentResourceContext().put(OLD_DEPLOYMENT_KEY, statefulSet);
}
- public static StatefulSet getCurrentStatefulSet(Context context) {
- return context.managedDependentResourceContext().getMandatory(OLD_DEPLOYMENT_KEY, StatefulSet.class);
+ public static Optional getCurrentStatefulSet(Context> context) {
+ return context.managedWorkflowAndDependentResourceContext().get(OLD_DEPLOYMENT_KEY, StatefulSet.class);
}
- public static void storeDesiredStatefulSet(Context context, StatefulSet statefulSet) {
- context.managedDependentResourceContext().put(NEW_DEPLOYMENT_KEY, statefulSet);
+ public static void storeDesiredStatefulSet(Context> context, StatefulSet statefulSet) {
+ context.managedWorkflowAndDependentResourceContext().put(NEW_DEPLOYMENT_KEY, statefulSet);
}
- public static StatefulSet getDesiredStatefulSet(Context context) {
- return context.managedDependentResourceContext().getMandatory(NEW_DEPLOYMENT_KEY, StatefulSet.class);
+ public static StatefulSet getDesiredStatefulSet(Context> context) {
+ return context.managedWorkflowAndDependentResourceContext().getMandatory(NEW_DEPLOYMENT_KEY, StatefulSet.class);
}
- public static void storeUpgradeType(Context context, UpgradeType upgradeType) {
- context.managedDependentResourceContext().put(UPGRADE_TYPE_KEY, upgradeType);
+ public static void storeUpgradeType(Context> context, UpgradeType upgradeType) {
+ context.managedWorkflowAndDependentResourceContext().put(UPGRADE_TYPE_KEY, upgradeType);
}
- public static Optional getUpgradeType(Context context) {
- return context.managedDependentResourceContext().get(UPGRADE_TYPE_KEY, UpgradeType.class);
+ public static Optional getUpgradeType(Context> context) {
+ return context.managedWorkflowAndDependentResourceContext().get(UPGRADE_TYPE_KEY, UpgradeType.class);
+ }
+
+ public static void storeOperatorConfig(Context> context, Config operatorConfig) {
+ context.managedWorkflowAndDependentResourceContext().put(OPERATOR_CONFIG_KEY, operatorConfig);
+ }
+
+ public static Config getOperatorConfig(Context> context) {
+ return context.managedWorkflowAndDependentResourceContext().getMandatory(OPERATOR_CONFIG_KEY, Config.class);
+ }
+
+ public static void storeWatchedResources(Context> context, WatchedResources watchedResources) {
+ context.managedWorkflowAndDependentResourceContext().put(WATCHED_RESOURCES_KEY, watchedResources);
+ }
+
+ public static WatchedResources getWatchedResources(Context> context) {
+ return context.managedWorkflowAndDependentResourceContext().getMandatory(WATCHED_RESOURCES_KEY, WatchedResources.class);
+ }
+
+ public static void storeDistConfigurator(Context> context, KeycloakDistConfigurator distConfigurator) {
+ context.managedWorkflowAndDependentResourceContext().put(DIST_CONFIGURATOR_KEY, distConfigurator);
+ }
+
+ public static KeycloakDistConfigurator getDistConfigurator(Context> context) {
+ return context.managedWorkflowAndDependentResourceContext().getMandatory(DIST_CONFIGURATOR_KEY, KeycloakDistConfigurator.class);
}
}
diff --git a/operator/src/main/java/org/keycloak/operator/Utils.java b/operator/src/main/java/org/keycloak/operator/Utils.java
index 7d8de6ef7df..65bb318b833 100644
--- a/operator/src/main/java/org/keycloak/operator/Utils.java
+++ b/operator/src/main/java/org/keycloak/operator/Utils.java
@@ -80,13 +80,6 @@ public final class Utils {
return labels;
}
- public static Optional getByName(Class clazz, Function nameFunction, Keycloak primary, Context context) {
- InformerEventSource ies = (InformerEventSource) context
- .eventSourceRetriever().getResourceEventSourceFor(clazz);
-
- return ies.get(new ResourceID(nameFunction.apply(primary), primary.getMetadata().getNamespace()));
- }
-
/**
* Set resources requests/limits for Keycloak container
*
diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakAdminSecretDependentResource.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakAdminSecretDependentResource.java
index 5d667884de7..c9786420cf0 100644
--- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakAdminSecretDependentResource.java
+++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakAdminSecretDependentResource.java
@@ -3,8 +3,8 @@ package org.keycloak.operator.controllers;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.SecretBuilder;
import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil;
+import io.javaoperatorsdk.operator.api.config.informer.Informer;
import io.javaoperatorsdk.operator.api.reconciler.Context;
-import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected;
import io.javaoperatorsdk.operator.processing.dependent.Creator;
@@ -20,22 +20,16 @@ import org.keycloak.operator.crds.v2alpha1.deployment.spec.BootstrapAdminSpec;
import java.util.Optional;
import java.util.UUID;
-@KubernetesDependent(labelSelector = Constants.DEFAULT_LABELS_AS_STRING, resourceDiscriminator = KeycloakAdminSecretDependentResource.NameResourceDiscriminator.class)
+@KubernetesDependent(
+ informer = @Informer(labelSelector = Constants.DEFAULT_LABELS_AS_STRING)
+)
public class KeycloakAdminSecretDependentResource extends KubernetesDependentResource implements Creator, GarbageCollected {
-
+
public static class EnabledCondition implements Condition {
@Override
public boolean isMet(DependentResource dependentResource, Keycloak primary,
Context context) {
- return Optional.ofNullable(primary.getSpec().getBootstrapAdminSpec()).map(BootstrapAdminSpec::getUser)
- .map(BootstrapAdminSpec.User::getSecret).filter(s -> !s.equals(KeycloakAdminSecretDependentResource.getName(primary))).isEmpty();
- }
- }
-
- public static class NameResourceDiscriminator implements ResourceDiscriminator {
- @Override
- public Optional distinguish(Class resource, Keycloak primary, Context context) {
- return Utils.getByName(Secret.class, KeycloakAdminSecretDependentResource::getName, primary, context);
+ return !hasCustomAdminSecret(primary);
}
}
@@ -62,4 +56,9 @@ public class KeycloakAdminSecretDependentResource extends KubernetesDependentRes
return KubernetesResourceUtil.sanitizeName(keycloak.getMetadata().getName() + "-initial-admin");
}
+ public static boolean hasCustomAdminSecret(Keycloak keycloak) {
+ return Optional.ofNullable(keycloak.getSpec().getBootstrapAdminSpec()).map(BootstrapAdminSpec::getUser)
+ .map(BootstrapAdminSpec.User::getSecret).filter(s -> !s.equals(KeycloakAdminSecretDependentResource.getName(keycloak))).isPresent();
+ }
+
}
diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakController.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakController.java
index 9333f8d7fd8..e27d9f142a3 100644
--- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakController.java
+++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakController.java
@@ -21,55 +21,50 @@ import io.fabric8.kubernetes.api.model.ContainerStateWaiting;
import io.fabric8.kubernetes.api.model.ContainerStatus;
import io.fabric8.kubernetes.api.model.PodSpec;
import io.fabric8.kubernetes.api.model.PodStatus;
-import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apps.StatefulSet;
import io.fabric8.kubernetes.client.readiness.Readiness;
import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil;
import io.fabric8.kubernetes.client.utils.Serialization;
-import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
import io.javaoperatorsdk.operator.api.reconciler.Context;
-import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
-import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusHandler;
import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusUpdateControl;
import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
-import io.javaoperatorsdk.operator.api.reconciler.EventSourceInitializer;
+import io.javaoperatorsdk.operator.api.reconciler.EventSourceUtils;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
+import io.javaoperatorsdk.operator.api.reconciler.Workflow;
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
-import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
-import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers;
import io.quarkus.logging.Log;
-
+import jakarta.inject.Inject;
import org.keycloak.common.util.CollectionUtil;
import org.keycloak.operator.Config;
import org.keycloak.operator.Constants;
+import org.keycloak.operator.ContextUtils;
import org.keycloak.operator.Utils;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
+import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakBuilder;
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatus;
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusAggregator;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.HostnameSpec;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.HostnameSpecBuilder;
+import org.keycloak.operator.upgrade.UpgradeLogicFactory;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
-import jakarta.inject.Inject;
-import org.keycloak.operator.upgrade.UpgradeLogicFactory;
-
-@ControllerConfiguration(
+@Workflow(
+ explicitInvocation = true,
dependents = {
+ @Dependent(type = KeycloakDeploymentDependentResource.class, reconcilePrecondition = KeycloakDeploymentDependentResource.ReconcilePrecondition.class),
@Dependent(type = KeycloakAdminSecretDependentResource.class, reconcilePrecondition = KeycloakAdminSecretDependentResource.EnabledCondition.class),
@Dependent(type = KeycloakIngressDependentResource.class, reconcilePrecondition = KeycloakIngressDependentResource.EnabledCondition.class),
- @Dependent(type = KeycloakServiceDependentResource.class, useEventSourceWithName = "serviceSource"),
- @Dependent(type = KeycloakDiscoveryServiceDependentResource.class, useEventSourceWithName = "serviceSource"),
+ @Dependent(type = KeycloakServiceDependentResource.class),
+ @Dependent(type = KeycloakDiscoveryServiceDependentResource.class),
@Dependent(type = KeycloakNetworkPolicyDependentResource.class, reconcilePrecondition = KeycloakNetworkPolicyDependentResource.EnabledCondition.class)
})
-public class KeycloakController implements Reconciler, EventSourceInitializer, ErrorStatusHandler {
+public class KeycloakController implements Reconciler {
public static final String OPENSHIFT_DEFAULT = "openshift-default";
@@ -85,32 +80,12 @@ public class KeycloakController implements Reconciler, EventSourceInit
@Inject
UpgradeLogicFactory upgradeLogicFactory;
- volatile KeycloakDeploymentDependentResource deploymentDependentResource;
- volatile KeycloakUpdateJobDependentResource updateJobDependentResource;
+ @Inject
+ KeycloakUpdateJobDependentResource updateJobDependentResource;
@Override
- public Map prepareEventSources(EventSourceContext context) {
- var namespaces = context.getControllerConfiguration().getNamespaces();
-
- InformerConfiguration servicesIC = InformerConfiguration
- .from(Service.class)
- .withLabelSelector(Constants.DEFAULT_LABELS_AS_STRING)
- .withNamespaces(namespaces)
- .withSecondaryToPrimaryMapper(Mappers.fromOwnerReference())
- .build();
-
- EventSource servicesEvent = new InformerEventSource<>(servicesIC, context);
-
- Map sources = new HashMap<>();
- sources.put("serviceSource", servicesEvent);
-
- this.deploymentDependentResource = new KeycloakDeploymentDependentResource(config, watchedResources, distConfigurator);
- sources.putAll(EventSourceInitializer.nameEventSourcesFromDependentResource(context, this.deploymentDependentResource));
-
- updateJobDependentResource = new KeycloakUpdateJobDependentResource(config);
- sources.putAll(EventSourceInitializer.nameEventSourcesFromDependentResource(context, updateJobDependentResource));
-
- return sources;
+ public List> prepareEventSources(EventSourceContext context) {
+ return EventSourceUtils.dependentEventSources(context, updateJobDependentResource);
}
@Override
@@ -141,10 +116,25 @@ public class KeycloakController implements Reconciler, EventSourceInit
}
if (modifiedSpec) {
- return UpdateControl.updateResource(kc);
+ // just patch spec using SSA, nothing more
+ Keycloak patchedKc = new KeycloakBuilder()
+ .withNewMetadata()
+ .withName(kc.getMetadata().getName())
+ .withNamespace(kc.getMetadata().getNamespace())
+ .endMetadata()
+ .withSpec(kc.getSpec())
+ .build();
+ return UpdateControl.patchResource(patchedKc);
}
- var upgradeLogicControl = upgradeLogicFactory.create(kc, context, deploymentDependentResource, updateJobDependentResource)
+ var existingDeployment = context.getSecondaryResource(StatefulSet.class).orElse(null);
+ ContextUtils.storeOperatorConfig(context, config);
+ ContextUtils.storeWatchedResources(context, watchedResources);
+ ContextUtils.storeDistConfigurator(context, distConfigurator);
+ ContextUtils.storeCurrentStatefulSet(context, existingDeployment);
+ ContextUtils.storeDesiredStatefulSet(context, new KeycloakDeploymentDependentResource().desired(kc, context));
+
+ var upgradeLogicControl = upgradeLogicFactory.create(kc, context)
.decideUpgrade();
if (upgradeLogicControl.isPresent()) {
Log.debug("--- Reconciliation interrupted due to upgrade logic");
@@ -152,11 +142,11 @@ public class KeycloakController implements Reconciler, EventSourceInit
}
// after the spec has possibly been updated, reconcile the StatefulSet
- this.deploymentDependentResource.reconcile(kc, context);
+ context.managedWorkflowAndDependentResourceContext().reconcileManagedWorkflow();
var statusAggregator = new KeycloakStatusAggregator(kc.getStatus(), kc.getMetadata().getGeneration());
- updateStatus(kc, context.getSecondaryResource(StatefulSet.class).orElse(null), statusAggregator, context);
+ updateStatus(kc, existingDeployment, statusAggregator, context);
var status = statusAggregator.build();
Log.debug("--- Reconciliation finished successfully");
@@ -167,7 +157,7 @@ public class KeycloakController implements Reconciler, EventSourceInit
}
else {
kc.setStatus(status);
- updateControl = UpdateControl.updateStatus(kc);
+ updateControl = UpdateControl.patchStatus(kc);
}
var statefulSet = context.getSecondaryResource(StatefulSet.class);
@@ -190,7 +180,7 @@ public class KeycloakController implements Reconciler, EventSourceInit
kc.setStatus(status);
- return ErrorStatusUpdateControl.updateStatus(kc);
+ return ErrorStatusUpdateControl.patchStatus(kc);
}
public static Optional generateOpenshiftHostname(Keycloak keycloak, Context context) {
diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeploymentDependentResource.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeploymentDependentResource.java
index 097df9f6558..3712924db01 100644
--- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeploymentDependentResource.java
+++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDeploymentDependentResource.java
@@ -31,11 +31,13 @@ import io.fabric8.kubernetes.api.model.VolumeMountBuilder;
import io.fabric8.kubernetes.api.model.apps.StatefulSet;
import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
+import io.javaoperatorsdk.operator.api.config.informer.Informer;
import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
-import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfigBuilder;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
+import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
import io.quarkus.logging.Log;
-
import org.keycloak.operator.Config;
import org.keycloak.operator.Constants;
import org.keycloak.operator.ContextUtils;
@@ -72,6 +74,9 @@ import static org.keycloak.operator.controllers.KeycloakDistConfigurator.getKeyc
import static org.keycloak.operator.crds.v2alpha1.CRDUtils.isTlsConfigured;
import static org.keycloak.operator.crds.v2alpha1.deployment.spec.TracingSpec.convertTracingAttributesToString;
+@KubernetesDependent(
+ informer = @Informer(labelSelector = Constants.DEFAULT_LABELS_AS_STRING)
+)
public class KeycloakDeploymentDependentResource extends CRUDKubernetesDependentResource {
public static final String POD_IP = "POD_IP";
@@ -95,23 +100,23 @@ public class KeycloakDeploymentDependentResource extends CRUDKubernetesDependent
public static final String OPTIMIZED_ARG = "--optimized";
- Config operatorConfig;
-
- WatchedResources watchedResources;
-
- KeycloakDistConfigurator distConfigurator;
-
private boolean useServiceCaCrt;
- public KeycloakDeploymentDependentResource(Config operatorConfig, WatchedResources watchedResources, KeycloakDistConfigurator distConfigurator) {
+ // Do not create the deployment before the initial admin secret is created to prevent the deployment from restarting.
+ // Not using native dependsOn as the initial admin secret may not be created by the operator and might be provided by the user,
+ // in which case we want to create the deployment immediately.
+ public static class ReconcilePrecondition implements Condition {
+ @Override
+ public boolean isMet(DependentResource dependentResource, Keycloak primary, Context context) {
+ return KeycloakAdminSecretDependentResource.hasCustomAdminSecret(primary)
+ || context.getSecondaryResourcesAsStream(Secret.class)
+ .anyMatch(s -> s.getMetadata().getName().equals(KeycloakAdminSecretDependentResource.getName(primary)));
+ }
+ }
+
+ public KeycloakDeploymentDependentResource() {
super(StatefulSet.class);
- this.operatorConfig = operatorConfig;
- this.watchedResources = watchedResources;
- this.distConfigurator = distConfigurator;
useServiceCaCrt = Files.exists(Path.of(SERVICE_CA_CRT));
- this.configureWith(new KubernetesDependentResourceConfigBuilder()
- .withLabelSelector(Constants.DEFAULT_LABELS_AS_STRING)
- .build());
}
public void setUseServiceCaCrt(boolean useServiceCaCrt) {
@@ -120,17 +125,20 @@ public class KeycloakDeploymentDependentResource extends CRUDKubernetesDependent
@Override
public StatefulSet desired(Keycloak primary, Context context) {
- StatefulSet baseDeployment = createBaseDeployment(primary, context);
+ Config operatorConfig = ContextUtils.getOperatorConfig(context);
+ WatchedResources watchedResources = ContextUtils.getWatchedResources(context);
+
+ StatefulSet baseDeployment = createBaseDeployment(primary, context, operatorConfig);
TreeSet allSecrets = new TreeSet<>();
if (isTlsConfigured(primary)) {
configureTLS(primary, baseDeployment, allSecrets);
}
Container kcContainer = baseDeployment.getSpec().getTemplate().getSpec().getContainers().get(0);
addTruststores(primary, baseDeployment, kcContainer, allSecrets);
- addEnvVars(baseDeployment, primary, allSecrets);
+ addEnvVars(baseDeployment, primary, allSecrets, context);
addResources(primary.getSpec().getResourceRequirements(), operatorConfig, kcContainer);
Optional.ofNullable(primary.getSpec().getCacheSpec())
- .ifPresent(c -> configureCache(baseDeployment, kcContainer, c, context.getClient()));
+ .ifPresent(c -> configureCache(baseDeployment, kcContainer, c, context.getClient(), watchedResources));
if (!allSecrets.isEmpty()) {
watchedResources.annotateDeployment(new ArrayList<>(allSecrets), Secret.class, baseDeployment, context.getClient());
@@ -142,7 +150,7 @@ public class KeycloakDeploymentDependentResource extends CRUDKubernetesDependent
return baseDeployment;
}
- var existingDeployment = ContextUtils.getCurrentStatefulSet(context);
+ var existingDeployment = ContextUtils.getCurrentStatefulSet(context).orElseThrow();
// version 22 changed the match labels, account for older versions
if (!existingDeployment.isMarkedForDeletion() && !hasExpectedMatchLabels(existingDeployment, primary)) {
@@ -156,7 +164,7 @@ public class KeycloakDeploymentDependentResource extends CRUDKubernetesDependent
};
}
- private void configureCache(StatefulSet deployment, Container kcContainer, CacheSpec spec, KubernetesClient client) {
+ private void configureCache(StatefulSet deployment, Container kcContainer, CacheSpec spec, KubernetesClient client, WatchedResources watchedResources) {
Optional.ofNullable(spec.getConfigMapFile()).ifPresent(configFile -> {
if (configFile.getName() == null || configFile.getKey() == null) {
throw new IllegalStateException("Cache file ConfigMap requires both a name and a key");
@@ -236,7 +244,7 @@ public class KeycloakDeploymentDependentResource extends CRUDKubernetesDependent
return Optional.ofNullable(keycloakCR.getSpec()).map(KeycloakSpec::getUnsupported).map(UnsupportedSpec::getPodTemplate);
}
- private StatefulSet createBaseDeployment(Keycloak keycloakCR, Context context) {
+ private StatefulSet createBaseDeployment(Keycloak keycloakCR, Context context, Config operatorConfig) {
Map labels = Utils.allInstanceLabels(keycloakCR);
labels.put("app.kubernetes.io/component", "server");
Map schedulingLabels = new LinkedHashMap<>(labels);
@@ -404,7 +412,8 @@ public class KeycloakDeploymentDependentResource extends CRUDKubernetesDependent
return JGROUPS_DNS_QUERY_PARAM + KeycloakDiscoveryServiceDependentResource.getName(keycloakCR) +"." + keycloakCR.getMetadata().getNamespace();
}
- private void addEnvVars(StatefulSet baseDeployment, Keycloak keycloakCR, TreeSet allSecrets) {
+ private void addEnvVars(StatefulSet baseDeployment, Keycloak keycloakCR, TreeSet allSecrets, Context context) {
+ var distConfigurator = ContextUtils.getDistConfigurator(context);
var firstClasssEnvVars = distConfigurator.configureDistOptions(keycloakCR);
var additionalEnvVars = getDefaultAndAdditionalEnvVars(keycloakCR);
diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDiscoveryServiceDependentResource.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDiscoveryServiceDependentResource.java
index 8ebe25b44d3..66417a5e54a 100644
--- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDiscoveryServiceDependentResource.java
+++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakDiscoveryServiceDependentResource.java
@@ -20,8 +20,8 @@ import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServiceBuilder;
import io.fabric8.kubernetes.api.model.ServiceSpec;
import io.fabric8.kubernetes.api.model.ServiceSpecBuilder;
+import io.javaoperatorsdk.operator.api.config.informer.Informer;
import io.javaoperatorsdk.operator.api.reconciler.Context;
-import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
@@ -29,18 +29,11 @@ import org.keycloak.operator.Constants;
import org.keycloak.operator.Utils;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
-import java.util.Optional;
-
-@KubernetesDependent(labelSelector = Constants.DEFAULT_LABELS_AS_STRING, resourceDiscriminator = KeycloakDiscoveryServiceDependentResource.NameResourceDiscriminator.class)
+@KubernetesDependent(
+ informer = @Informer(labelSelector = Constants.DEFAULT_LABELS_AS_STRING)
+)
public class KeycloakDiscoveryServiceDependentResource extends CRUDKubernetesDependentResource {
- public static class NameResourceDiscriminator implements ResourceDiscriminator {
- @Override
- public Optional distinguish(Class resource, Keycloak primary, Context context) {
- return Utils.getByName(Service.class, KeycloakDiscoveryServiceDependentResource::getName, primary, context);
- }
- }
-
public KeycloakDiscoveryServiceDependentResource() {
super(Service.class);
}
diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakIngressDependentResource.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakIngressDependentResource.java
index 01d28b4415c..822cbcc7e11 100644
--- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakIngressDependentResource.java
+++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakIngressDependentResource.java
@@ -19,6 +19,7 @@ package org.keycloak.operator.controllers;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.api.model.networking.v1.IngressBuilder;
import io.fabric8.kubernetes.client.KubernetesClientException;
+import io.javaoperatorsdk.operator.api.config.informer.Informer;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
@@ -39,7 +40,9 @@ import java.util.Optional;
import static org.keycloak.operator.crds.v2alpha1.CRDUtils.isTlsConfigured;
-@KubernetesDependent(labelSelector = Constants.DEFAULT_LABELS_AS_STRING)
+@KubernetesDependent(
+ informer = @Informer(labelSelector = Constants.DEFAULT_LABELS_AS_STRING)
+)
public class KeycloakIngressDependentResource extends CRUDKubernetesDependentResource {
private static final Logger LOG = Logger.getLogger(KeycloakIngressDependentResource.class.getName());
diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakNetworkPolicyDependentResource.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakNetworkPolicyDependentResource.java
index f6ba14cb64c..630d875692c 100644
--- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakNetworkPolicyDependentResource.java
+++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakNetworkPolicyDependentResource.java
@@ -26,6 +26,7 @@ import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicy;
import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicyBuilder;
import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicyFluent;
import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicyPeer;
+import io.javaoperatorsdk.operator.api.config.informer.Informer;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
@@ -46,7 +47,9 @@ import static org.keycloak.operator.Constants.KEYCLOAK_JGROUPS_FD_PORT;
import static org.keycloak.operator.Constants.KEYCLOAK_JGROUPS_PROTOCOL;
import static org.keycloak.operator.Constants.KEYCLOAK_SERVICE_PROTOCOL;
-@KubernetesDependent(labelSelector = Constants.DEFAULT_LABELS_AS_STRING)
+@KubernetesDependent(
+ informer = @Informer(labelSelector = Constants.DEFAULT_LABELS_AS_STRING)
+)
public class KeycloakNetworkPolicyDependentResource extends CRUDKubernetesDependentResource {
private static final Logger LOG = Logger.getLogger(KeycloakNetworkPolicyDependentResource.class.getName());
diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportController.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportController.java
index dcbda489fa4..4e137797643 100644
--- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportController.java
+++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportController.java
@@ -21,49 +21,34 @@ import io.fabric8.kubernetes.api.model.apps.StatefulSetStatus;
import io.fabric8.kubernetes.api.model.batch.v1.Job;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.javaoperatorsdk.operator.api.reconciler.Context;
-import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
-import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusHandler;
import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusUpdateControl;
-import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
-import io.javaoperatorsdk.operator.api.reconciler.EventSourceInitializer;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
+import io.javaoperatorsdk.operator.api.reconciler.Workflow;
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
-import io.javaoperatorsdk.operator.processing.event.source.EventSource;
import io.quarkus.logging.Log;
-
+import jakarta.inject.Inject;
import org.keycloak.operator.Config;
+import org.keycloak.operator.ContextUtils;
import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImport;
import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImportStatus;
import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImportStatusBuilder;
import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImportStatusCondition;
-import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
-import jakarta.inject.Inject;
-
-@ControllerConfiguration(
+@Workflow(
+explicitInvocation = true,
dependents = {
- @Dependent(type = KeycloakRealmImportSecretDependentResource.class)
+ @Dependent(type = KeycloakRealmImportJobDependentResource.class, dependsOn = KeycloakRealmImportSecretDependentResource.DEPENDENT_NAME),
+ @Dependent(type = KeycloakRealmImportSecretDependentResource.class, name = KeycloakRealmImportSecretDependentResource.DEPENDENT_NAME)
})
-public class KeycloakRealmImportController implements Reconciler, ErrorStatusHandler, EventSourceInitializer {
+public class KeycloakRealmImportController implements Reconciler {
@Inject
Config config;
- @Inject
- KubernetesClient client;
-
- volatile KeycloakRealmImportJobDependentResource jobDependentResource;
-
- @Override
- public Map prepareEventSources(EventSourceContext context) {
- this.jobDependentResource = new KeycloakRealmImportJobDependentResource(config);
- return EventSourceInitializer.nameEventSourcesFromDependentResource(context, jobDependentResource);
- }
-
@Override
public UpdateControl reconcile(KeycloakRealmImport realm, Context context) {
String realmName = realm.getMetadata().getName();
@@ -78,13 +63,14 @@ public class KeycloakRealmImportController implements Reconciler 0) {
- jobDependentResource.reconcile(realm, context);
+ context.managedWorkflowAndDependentResourceContext().reconcileManagedWorkflow();
}
}
- updateStatus(statusBuilder, realm, existingJob, existingDeployment);
+ updateStatus(statusBuilder, realm, existingJob, existingDeployment, context.getClient());
var status = statusBuilder.build();
@@ -95,7 +81,7 @@ public class KeycloakRealmImportController implements Reconciler 0) {
if (!lastReportedStatus.isDone()) {
Log.info("Job finished performing a rolling restart of the deployment");
- rollingRestart(realmCR); // could be based upon a hash annotation on the deployment instead
+ rollingRestart(realmCR, client); // could be based upon a hash annotation on the deployment instead
}
status.addDone();
} else if (oldStatus.getFailed() != null && oldStatus.getFailed() > 0) {
@@ -161,7 +147,7 @@ public class KeycloakRealmImportController implements Reconciler implements Creator, GarbageCollected {
- private final Config config;
-
- KeycloakRealmImportJobDependentResource(Config config) {
+ KeycloakRealmImportJobDependentResource() {
super(Job.class);
- this.config = config;
- this.configureWith(new KubernetesDependentResourceConfigBuilder()
- .withLabelSelector(Constants.DEFAULT_LABELS_AS_STRING)
- .build());
}
@Override
protected Job desired(KeycloakRealmImport primary, Context context) {
- StatefulSet existingDeployment = context.managedDependentResourceContext().get(StatefulSet.class, StatefulSet.class).orElseThrow();
+ Config config = ContextUtils.getOperatorConfig(context);
+ StatefulSet existingDeployment = ContextUtils.getCurrentStatefulSet(context).orElseThrow();
Map placeholders = primary.getSpec().getPlaceholders();
boolean replacePlaceholders = (placeholders != null && !placeholders.isEmpty());
@@ -71,7 +66,7 @@ public class KeycloakRealmImportJobDependentResource extends KubernetesDependent
String secretName = KeycloakRealmImportSecretDependentResource.getSecretName(primary);
String volumeName = KubernetesResourceUtil.sanitizeName(secretName + "-volume");
- buildKeycloakJobContainer(keycloakPodTemplate.getSpec().getContainers().get(0), primary, volumeName, replacePlaceholders);
+ buildKeycloakJobContainer(keycloakPodTemplate.getSpec().getContainers().get(0), primary, volumeName, config);
keycloakPodTemplate.getSpec().getVolumes().add(buildSecretVolume(volumeName, secretName));
var labels = keycloakPodTemplate.getMetadata().getLabels();
@@ -138,7 +133,7 @@ public class KeycloakRealmImportJobDependentResource extends KubernetesDependent
.build();
}
- private void buildKeycloakJobContainer(Container keycloakContainer, KeycloakRealmImport keycloakRealmImport, String volumeName, boolean replacePlaceholders) {
+ private void buildKeycloakJobContainer(Container keycloakContainer, KeycloakRealmImport keycloakRealmImport, String volumeName, Config config) {
var importMntPath = "/mnt/realm-import/";
var command = List.of("/bin/bash");
diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportSecretDependentResource.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportSecretDependentResource.java
index 16a0d40e183..0db50623d94 100644
--- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportSecretDependentResource.java
+++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportSecretDependentResource.java
@@ -3,6 +3,7 @@ package org.keycloak.operator.controllers;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.SecretBuilder;
import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil;
+import io.javaoperatorsdk.operator.api.config.informer.Informer;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
@@ -11,9 +12,13 @@ import org.keycloak.operator.Constants;
import org.keycloak.operator.Utils;
import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImport;
-@KubernetesDependent(labelSelector = Constants.DEFAULT_LABELS_AS_STRING)
+@KubernetesDependent(
+ informer = @Informer(labelSelector = Constants.DEFAULT_LABELS_AS_STRING)
+)
public class KeycloakRealmImportSecretDependentResource extends CRUDKubernetesDependentResource {
+ public static final String DEPENDENT_NAME = "realm-import-secret";
+
public KeycloakRealmImportSecretDependentResource() {
super(Secret.class);
}
diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakServiceDependentResource.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakServiceDependentResource.java
index 5aefe119f37..7ed3549d2a3 100644
--- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakServiceDependentResource.java
+++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakServiceDependentResource.java
@@ -23,8 +23,8 @@ import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServiceBuilder;
import io.fabric8.kubernetes.api.model.ServiceSpec;
import io.fabric8.kubernetes.api.model.ServiceSpecBuilder;
+import io.javaoperatorsdk.operator.api.config.informer.Informer;
import io.javaoperatorsdk.operator.api.reconciler.Context;
-import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
import org.keycloak.operator.Constants;
@@ -35,16 +35,11 @@ import org.keycloak.operator.crds.v2alpha1.deployment.spec.HttpSpec;
import static org.keycloak.operator.crds.v2alpha1.CRDUtils.isTlsConfigured;
-@KubernetesDependent(labelSelector = Constants.DEFAULT_LABELS_AS_STRING, resourceDiscriminator = KeycloakServiceDependentResource.NameResourceDiscriminator.class)
+@KubernetesDependent(
+ informer = @Informer(labelSelector = Constants.DEFAULT_LABELS_AS_STRING)
+)
public class KeycloakServiceDependentResource extends CRUDKubernetesDependentResource {
- public static class NameResourceDiscriminator implements ResourceDiscriminator {
- @Override
- public Optional distinguish(Class resource, Keycloak primary, Context context) {
- return Utils.getByName(Service.class, KeycloakServiceDependentResource::getServiceName, primary, context);
- }
- }
-
public KeycloakServiceDependentResource() {
super(Service.class);
}
diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakUpdateJobDependentResource.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakUpdateJobDependentResource.java
index 41421c18b48..fbcc448c682 100644
--- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakUpdateJobDependentResource.java
+++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakUpdateJobDependentResource.java
@@ -36,16 +36,18 @@ import io.fabric8.kubernetes.api.model.Volume;
import io.fabric8.kubernetes.api.model.batch.v1.Job;
import io.fabric8.kubernetes.api.model.batch.v1.JobBuilder;
import io.fabric8.kubernetes.api.model.batch.v1.JobSpecFluent;
+import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfigBuilder;
-import org.keycloak.operator.Config;
+import jakarta.enterprise.context.ApplicationScoped;
import org.keycloak.operator.Constants;
import org.keycloak.operator.ContextUtils;
import org.keycloak.operator.Utils;
import org.keycloak.operator.crds.v2alpha1.CRDUtils;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
+@ApplicationScoped
public class KeycloakUpdateJobDependentResource extends CRUDKubernetesDependentResource {
// shared volume configuration
@@ -71,13 +73,12 @@ public class KeycloakUpdateJobDependentResource extends CRUDKubernetesDependentR
// container args to replace
private static final Set START_ARGS = Set.of("start", "start-dev");
- private final Config operatorConfig;
-
- public KeycloakUpdateJobDependentResource(Config config) {
+ public KeycloakUpdateJobDependentResource() {
super(Job.class);
- operatorConfig = config;
this.configureWith(new KubernetesDependentResourceConfigBuilder()
- .withLabelSelector(Constants.DEFAULT_LABELS_AS_STRING)
+ .withKubernetesDependentInformerConfig(InformerConfiguration.builder(resourceType())
+ .withLabelSelector(Constants.DEFAULT_LABELS_AS_STRING)
+ .build())
.build());
}
@@ -144,12 +145,12 @@ public class KeycloakUpdateJobDependentResource extends CRUDKubernetesDependentR
// For test KeycloakDeploymentTest#testDeploymentDurability
// it uses a pause image, which never ends.
// After this seconds, the job is terminated allowing the test to complete.
- builder.withActiveDeadlineSeconds(operatorConfig.keycloak().updatePodDeadlineSeconds());
+ builder.withActiveDeadlineSeconds(ContextUtils.getOperatorConfig(context).keycloak().updatePodDeadlineSeconds());
return builder.build();
}
private static void addInitContainer(PodSpecBuilder builder, Context context, Collection availableVolumes, Collection requiredVolumes) {
- var existing = CRDUtils.firstContainerOf(ContextUtils.getCurrentStatefulSet(context)).orElseThrow();
+ var existing = CRDUtils.firstContainerOf(ContextUtils.getCurrentStatefulSet(context).orElseThrow()).orElseThrow();
var containerBuilder = builder.addNewInitContainerLike(existing);
configureContainer(containerBuilder, INIT_CONTAINER_NAME, INIT_CONTAINER_ARGS, availableVolumes, requiredVolumes);
containerBuilder.endInitContainer();
@@ -196,7 +197,7 @@ public class KeycloakUpdateJobDependentResource extends CRUDKubernetesDependentR
private Map getAllVolumes(Context context) {
Map allVolumes = new HashMap<>();
Consumer volumeConsumer = volume -> allVolumes.put(volume.getName(), volume);
- CRDUtils.volumesFromStatefulSet(ContextUtils.getCurrentStatefulSet(context)).forEach(volumeConsumer);
+ CRDUtils.volumesFromStatefulSet(ContextUtils.getCurrentStatefulSet(context).orElseThrow()).forEach(volumeConsumer);
CRDUtils.volumesFromStatefulSet(ContextUtils.getDesiredStatefulSet(context)).forEach(volumeConsumer);
return allVolumes;
}
diff --git a/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/KeycloakStatus.java b/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/KeycloakStatus.java
index aa9c9604406..5eb24f6b9fa 100644
--- a/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/KeycloakStatus.java
+++ b/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/KeycloakStatus.java
@@ -24,14 +24,13 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import io.fabric8.kubernetes.model.annotation.LabelSelector;
import io.fabric8.kubernetes.model.annotation.StatusReplicas;
-import io.javaoperatorsdk.operator.api.ObservedGenerationAware;
import io.sundr.builder.annotations.Buildable;
/**
* @author Vaclav Muzikar
*/
@Buildable(editableEnabled = false, builderPackage = "io.fabric8.kubernetes.api.builder", lazyCollectionInitEnabled = false)
-public class KeycloakStatus implements ObservedGenerationAware {
+public class KeycloakStatus {
@LabelSelector
private String selector;
@@ -77,12 +76,10 @@ public class KeycloakStatus implements ObservedGenerationAware {
return findCondition(KeycloakStatusCondition.READY).map(KeycloakStatusCondition::getStatus).orElse(false);
}
- @Override
public Long getObservedGeneration() {
return observedGeneration;
}
- @Override
public void setObservedGeneration(Long generation) {
this.observedGeneration = generation;
}
diff --git a/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/KeycloakStatusAggregator.java b/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/KeycloakStatusAggregator.java
index 892327f16b6..83a5d8533d5 100644
--- a/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/KeycloakStatusAggregator.java
+++ b/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/KeycloakStatusAggregator.java
@@ -64,7 +64,6 @@ public class KeycloakStatusAggregator {
existingConditions = Map.of();
}
- // we're not setting this on the statusBuilder as we're letting the sdk manage that
observedGeneration = generation;
readyCondition.setType(KeycloakStatusCondition.READY);
@@ -144,7 +143,10 @@ public class KeycloakStatusAggregator {
updateConditionFromExisting(hasErrorsCondition, existingConditions, now);
updateConditionFromExisting(rollingUpdate, existingConditions, now);
- return statusBuilder.withConditions(List.of(readyCondition, hasErrorsCondition, rollingUpdate)).build();
+ return statusBuilder
+ .withObservedGeneration(observedGeneration)
+ .withConditions(List.of(readyCondition, hasErrorsCondition, rollingUpdate))
+ .build();
}
static void updateConditionFromExisting(KeycloakStatusCondition condition, Map existingConditions, String now) {
diff --git a/operator/src/main/java/org/keycloak/operator/upgrade/UpgradeLogicFactory.java b/operator/src/main/java/org/keycloak/operator/upgrade/UpgradeLogicFactory.java
index 98f15415adc..7f0cdf1b347 100644
--- a/operator/src/main/java/org/keycloak/operator/upgrade/UpgradeLogicFactory.java
+++ b/operator/src/main/java/org/keycloak/operator/upgrade/UpgradeLogicFactory.java
@@ -19,7 +19,7 @@ package org.keycloak.operator.upgrade;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import jakarta.enterprise.context.ApplicationScoped;
-import org.keycloak.operator.controllers.KeycloakDeploymentDependentResource;
+import jakarta.inject.Inject;
import org.keycloak.operator.controllers.KeycloakUpdateJobDependentResource;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.UpdateSpec;
@@ -32,13 +32,15 @@ import org.keycloak.operator.upgrade.impl.RecreateOnImageChangeUpgradeLogic;
*/
@ApplicationScoped
public class UpgradeLogicFactory {
+ @Inject
+ KeycloakUpdateJobDependentResource updateJobDependentResource;
- public UpgradeLogic create(Keycloak keycloak, Context context, KeycloakDeploymentDependentResource dependentResource, KeycloakUpdateJobDependentResource updateJobDependentResource) {
+ public UpgradeLogic create(Keycloak keycloak, Context context) {
var strategy = UpdateSpec.getUpdateStrategy(keycloak);
return switch (strategy) {
- case RECREATE_ON_IMAGE_CHANGE -> new RecreateOnImageChangeUpgradeLogic(context, keycloak, dependentResource);
- case FORCE_RECREATE -> new ForceRecreateUpgradeLogic(context, keycloak, dependentResource);
- case AUTO -> new AutoUpgradeLogic(context, keycloak, dependentResource, updateJobDependentResource);
+ case RECREATE_ON_IMAGE_CHANGE -> new RecreateOnImageChangeUpgradeLogic(context, keycloak);
+ case FORCE_RECREATE -> new ForceRecreateUpgradeLogic(context, keycloak);
+ case AUTO -> new AutoUpgradeLogic(context, keycloak, updateJobDependentResource);
};
}
diff --git a/operator/src/main/java/org/keycloak/operator/upgrade/impl/AutoUpgradeLogic.java b/operator/src/main/java/org/keycloak/operator/upgrade/impl/AutoUpgradeLogic.java
index 8c018cc0e01..6becfb7fb0d 100644
--- a/operator/src/main/java/org/keycloak/operator/upgrade/impl/AutoUpgradeLogic.java
+++ b/operator/src/main/java/org/keycloak/operator/upgrade/impl/AutoUpgradeLogic.java
@@ -32,7 +32,6 @@ import io.fabric8.kubernetes.client.KubernetesClient;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
import io.quarkus.logging.Log;
-import org.keycloak.operator.controllers.KeycloakDeploymentDependentResource;
import org.keycloak.operator.controllers.KeycloakUpdateJobDependentResource;
import org.keycloak.operator.crds.v2alpha1.CRDUtils;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
@@ -41,8 +40,8 @@ public class AutoUpgradeLogic extends BaseUpgradeLogic {
private final KeycloakUpdateJobDependentResource updateJobResource;
- public AutoUpgradeLogic(Context context, Keycloak keycloak, KeycloakDeploymentDependentResource statefulSetResource, KeycloakUpdateJobDependentResource updateJobResource) {
- super(context, keycloak, statefulSetResource);
+ public AutoUpgradeLogic(Context context, Keycloak keycloak, KeycloakUpdateJobDependentResource updateJobResource) {
+ super(context, keycloak);
this.updateJobResource = updateJobResource;
}
diff --git a/operator/src/main/java/org/keycloak/operator/upgrade/impl/BaseUpgradeLogic.java b/operator/src/main/java/org/keycloak/operator/upgrade/impl/BaseUpgradeLogic.java
index ade25411058..a3a3a1e2871 100644
--- a/operator/src/main/java/org/keycloak/operator/upgrade/impl/BaseUpgradeLogic.java
+++ b/operator/src/main/java/org/keycloak/operator/upgrade/impl/BaseUpgradeLogic.java
@@ -46,23 +46,21 @@ abstract class BaseUpgradeLogic implements UpgradeLogic {
protected final Context context;
protected final Keycloak keycloak;
- protected final KeycloakDeploymentDependentResource statefulSetResource;
- BaseUpgradeLogic(Context context, Keycloak keycloak, KeycloakDeploymentDependentResource statefulSetResource) {
+ BaseUpgradeLogic(Context context, Keycloak keycloak) {
this.context = context;
this.keycloak = keycloak;
- this.statefulSetResource = statefulSetResource;
}
@Override
public final Optional> decideUpgrade() {
- var existing = context.getSecondaryResource(StatefulSet.class);
+ var existing = ContextUtils.getCurrentStatefulSet(context);
if (existing.isEmpty()) {
// new deployment, no upgrade needed
Log.debug("New deployment - skipping upgrade logic");
return Optional.empty();
}
- var desiredStatefulSet = statefulSetResource.desired(keycloak, context);
+ var desiredStatefulSet = ContextUtils.getDesiredStatefulSet(context);
var desiredContainer = CRDUtils.firstContainerOf(desiredStatefulSet).orElseThrow(BaseUpgradeLogic::containerNotFound);
var actualContainer = CRDUtils.firstContainerOf(existing.get()).orElseThrow(BaseUpgradeLogic::containerNotFound);
@@ -72,9 +70,6 @@ abstract class BaseUpgradeLogic implements UpgradeLogic {
return Optional.empty();
}
- // store in context the current and desired stateful set for easy access.
- ContextUtils.storeCurrentStatefulSet(context, existing.get());
- ContextUtils.storeDesiredStatefulSet(context, desiredStatefulSet);
return onUpgrade();
}
diff --git a/operator/src/main/java/org/keycloak/operator/upgrade/impl/ForceRecreateUpgradeLogic.java b/operator/src/main/java/org/keycloak/operator/upgrade/impl/ForceRecreateUpgradeLogic.java
index 5387fe6e690..e0508c2ac53 100644
--- a/operator/src/main/java/org/keycloak/operator/upgrade/impl/ForceRecreateUpgradeLogic.java
+++ b/operator/src/main/java/org/keycloak/operator/upgrade/impl/ForceRecreateUpgradeLogic.java
@@ -21,7 +21,6 @@ import java.util.Optional;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
-import org.keycloak.operator.controllers.KeycloakDeploymentDependentResource;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
import org.keycloak.operator.upgrade.UpgradeLogic;
import org.keycloak.operator.upgrade.UpgradeType;
@@ -32,8 +31,8 @@ import org.keycloak.operator.upgrade.UpgradeType;
*/
public class ForceRecreateUpgradeLogic extends BaseUpgradeLogic {
- public ForceRecreateUpgradeLogic(Context context, Keycloak keycloak, KeycloakDeploymentDependentResource statefulSetResource) {
- super(context, keycloak, statefulSetResource);
+ public ForceRecreateUpgradeLogic(Context context, Keycloak keycloak) {
+ super(context, keycloak);
}
@Override
diff --git a/operator/src/main/java/org/keycloak/operator/upgrade/impl/RecreateOnImageChangeUpgradeLogic.java b/operator/src/main/java/org/keycloak/operator/upgrade/impl/RecreateOnImageChangeUpgradeLogic.java
index 6404ff4bd10..12a8ffb000b 100644
--- a/operator/src/main/java/org/keycloak/operator/upgrade/impl/RecreateOnImageChangeUpgradeLogic.java
+++ b/operator/src/main/java/org/keycloak/operator/upgrade/impl/RecreateOnImageChangeUpgradeLogic.java
@@ -25,7 +25,6 @@ import io.fabric8.kubernetes.api.model.apps.StatefulSet;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
import org.keycloak.operator.ContextUtils;
-import org.keycloak.operator.controllers.KeycloakDeploymentDependentResource;
import org.keycloak.operator.crds.v2alpha1.CRDUtils;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
import org.keycloak.operator.upgrade.UpgradeType;
@@ -38,13 +37,13 @@ import org.keycloak.operator.upgrade.UpgradeType;
@SuppressWarnings("ALL")
public class RecreateOnImageChangeUpgradeLogic extends BaseUpgradeLogic {
- public RecreateOnImageChangeUpgradeLogic(Context context, Keycloak keycloak, KeycloakDeploymentDependentResource dependentResource) {
- super(context, keycloak, dependentResource);
+ public RecreateOnImageChangeUpgradeLogic(Context context, Keycloak keycloak) {
+ super(context, keycloak);
}
@Override
Optional> onUpgrade() {
- var currentImage = extractImage(ContextUtils.getCurrentStatefulSet(context));
+ var currentImage = extractImage(ContextUtils.getCurrentStatefulSet(context).orElseThrow());
var desiredImage = extractImage(ContextUtils.getDesiredStatefulSet(context));
if (Objects.equals(currentImage, desiredImage)) {
diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java
index fc2bed68e9c..1b3b889c4ea 100644
--- a/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java
+++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java
@@ -62,7 +62,10 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInfo;
import org.keycloak.operator.Constants;
+import org.keycloak.operator.controllers.KeycloakController;
import org.keycloak.operator.controllers.KeycloakDeploymentDependentResource;
+import org.keycloak.operator.controllers.KeycloakRealmImportController;
+import org.keycloak.operator.controllers.KeycloakUpdateJobDependentResource;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakSpecBuilder;
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatus;
@@ -213,24 +216,6 @@ public class BaseOperatorTest implements QuarkusTestAfterEachCallback {
public KubernetesClient getKubernetesClient() {
return k8sclient;
}
-
- @Override
- public DependentResourceFactory dependentResourceFactory() {
- return new DependentResourceFactory>() {
- @Override
- public DependentResource createFrom(DependentResourceSpec spec,
- ControllerConfiguration> configuration) {
- final var dependentResourceClass = spec.getDependentResourceClass();
- // workaround for https://github.com/operator-framework/java-operator-sdk/issues/2010
- // create a fresh instance of the dependentresource
- CDI.current().destroy(CDI.current().select(dependentResourceClass).get());
- DependentResource instance = (DependentResource) CDI.current().select(dependentResourceClass).get();
- var context = Utils.contextFor(configuration, dependentResourceClass, Dependent.class);
- DependentResourceConfigurationResolver.configure(instance, spec, configuration);
- return instance;
- }
- };
- }
});
}
@@ -449,6 +434,11 @@ public class BaseOperatorTest implements QuarkusTestAfterEachCallback {
Log.info("Stopping Operator");
operator.stop();
+ // Avoid issues with Event Informers between tests
+ Log.info("Removing Controllers and application scoped DRs from CDI");
+ Stream.of(KeycloakController.class, KeycloakRealmImportController.class, KeycloakUpdateJobDependentResource.class)
+ .forEach(c -> CDI.current().destroy(CDI.current().select(c).get()));
+
Log.info("Creating new K8s Client");
// create a new client bc operator has closed the old one
createK8sClient();
diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/unit/KeycloakControllerTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/unit/KeycloakControllerTest.java
index 3fb51fa43fe..9c6b3c37c5b 100644
--- a/operator/src/test/java/org/keycloak/operator/testsuite/unit/KeycloakControllerTest.java
+++ b/operator/src/test/java/org/keycloak/operator/testsuite/unit/KeycloakControllerTest.java
@@ -53,9 +53,9 @@ class KeycloakControllerTest {
// both the instances and hostname should be updated
UpdateControl update = controller.reconcile(kc, mockContext);
- assertTrue(update.isUpdateResource());
- assertEquals(1, update.getResource().getSpec().getInstances());
- assertEquals("example-kc-ingress-ns.openshift.com", update.getResource().getSpec().getHostnameSpec().getHostname());
+ assertTrue(update.isPatchResource());
+ assertEquals(1, update.getResource().orElseThrow().getSpec().getInstances());
+ assertEquals("example-kc-ingress-ns.openshift.com", update.getResource().orElseThrow().getSpec().getHostnameSpec().getHostname());
// just the instances should be updated if not openshift-default
kc = K8sUtils.getDefaultKeycloakDeployment();
@@ -63,9 +63,9 @@ class KeycloakControllerTest {
kc.getSpec().setInstances(null);
kc.getSpec().getHostnameSpec().setHostname(null);
update = controller.reconcile(kc, mockContext);
- assertTrue(update.isUpdateResource());
- assertEquals(1, update.getResource().getSpec().getInstances());
- assertNull(update.getResource().getSpec().getHostnameSpec().getHostname());
+ assertTrue(update.isPatchResource());
+ assertEquals(1, update.getResource().orElseThrow().getSpec().getInstances());
+ assertNull(update.getResource().orElseThrow().getSpec().getHostnameSpec().getHostname());
}
}
diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/unit/PodTemplateTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/unit/PodTemplateTest.java
index beaf9ba6407..cffba0c04d7 100644
--- a/operator/src/test/java/org/keycloak/operator/testsuite/unit/PodTemplateTest.java
+++ b/operator/src/test/java/org/keycloak/operator/testsuite/unit/PodTemplateTest.java
@@ -32,12 +32,13 @@ import io.fabric8.kubernetes.api.model.Volume;
import io.fabric8.kubernetes.api.model.VolumeMount;
import io.fabric8.kubernetes.api.model.apps.StatefulSet;
import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder;
+import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.utils.Serialization;
import io.javaoperatorsdk.operator.api.reconciler.Context;
-import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DefaultManagedDependentResourceContext;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedWorkflowAndDependentResourceContext;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
-
+import jakarta.inject.Inject;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -57,17 +58,18 @@ import org.keycloak.operator.crds.v2alpha1.deployment.spec.UnsupportedSpec;
import org.mockito.Mockito;
import java.util.List;
-import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
-import jakarta.inject.Inject;
-
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.keycloak.operator.ContextUtils.DIST_CONFIGURATOR_KEY;
+import static org.keycloak.operator.ContextUtils.OLD_DEPLOYMENT_KEY;
+import static org.keycloak.operator.ContextUtils.OPERATOR_CONFIG_KEY;
+import static org.keycloak.operator.ContextUtils.WATCHED_RESOURCES_KEY;
@QuarkusTest
public class PodTemplateTest {
@@ -85,7 +87,7 @@ public class PodTemplateTest {
@BeforeEach
protected void setup() {
- this.deployment = new KeycloakDeploymentDependentResource(operatorConfig, watchedResources, distConfigurator);
+ this.deployment = new KeycloakDeploymentDependentResource();
}
private StatefulSet getDeployment(PodTemplateSpec podTemplate, StatefulSet existingDeployment, Consumer additionalSpec) {
@@ -108,12 +110,15 @@ public class PodTemplateTest {
kc.setSpec(keycloakSpecBuilder.build());
- var managedDependentResourceContext = new DefaultManagedDependentResourceContext();
//noinspection unchecked
Context context = Mockito.mock(Context.class);
- Mockito.when(context.managedDependentResourceContext()).thenReturn(managedDependentResourceContext);
- Mockito.when(context.getSecondaryResource(StatefulSet.class)).thenReturn(Optional.ofNullable(existingDeployment));
-
+ ManagedWorkflowAndDependentResourceContext managedWorkflowAndDependentResourceContext = Mockito.mock(ManagedWorkflowAndDependentResourceContext.class);
+ Mockito.when(context.managedWorkflowAndDependentResourceContext()).thenReturn(managedWorkflowAndDependentResourceContext);
+ Mockito.when(managedWorkflowAndDependentResourceContext.getMandatory(OLD_DEPLOYMENT_KEY, StatefulSet.class)).thenReturn(existingDeployment);
+ Mockito.when(managedWorkflowAndDependentResourceContext.getMandatory(OPERATOR_CONFIG_KEY, Config.class)).thenReturn(operatorConfig);
+ Mockito.when(managedWorkflowAndDependentResourceContext.getMandatory(WATCHED_RESOURCES_KEY, WatchedResources.class)).thenReturn(watchedResources);
+ Mockito.when(managedWorkflowAndDependentResourceContext.getMandatory(DIST_CONFIGURATOR_KEY, KeycloakDistConfigurator.class)).thenReturn(distConfigurator);
+ Mockito.when(context.getClient()).thenReturn(Mockito.mock(KubernetesClient.class));
return deployment.desired(kc, context);
}
diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/utils/CRAssert.java b/operator/src/test/java/org/keycloak/operator/testsuite/utils/CRAssert.java
index cc9a6b3f1dd..3b1d162c0c7 100644
--- a/operator/src/test/java/org/keycloak/operator/testsuite/utils/CRAssert.java
+++ b/operator/src/test/java/org/keycloak/operator/testsuite/utils/CRAssert.java
@@ -120,6 +120,7 @@ public final class CRAssert {
.await()
.pollInterval(1, TimeUnit.SECONDS)
.timeout(Duration.ofMinutes(5))
+ .ignoreExceptions()
.untilAsserted(() -> client.pods()
.inNamespace(namespaceOf(keycloak))
.withLabels(Utils.allInstanceLabels(keycloak))
diff --git a/pom.xml b/pom.xml
index 06ec4a02da4..e6ef38e753d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,8 +52,8 @@
jboss-snapshots-repository
https://s01.oss.sonatype.org/content/repositories/snapshots/
- 3.17.8
- 3.17.8
+ 3.18.2
+ 3.18.2
${timestamp}
@@ -165,7 +165,7 @@
mirror.gcr.io/postgres:${postgresql.version}
16.1
2.3.1
- 42.7.4
+ 42.7.5
11.4
mirror.gcr.io/mariadb:${mariadb.version}
3.4.1
diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ProxyHostnameV2DistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ProxyHostnameV2DistTest.java
index 72062e64d00..72358da2478 100644
--- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ProxyHostnameV2DistTest.java
+++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ProxyHostnameV2DistTest.java
@@ -117,9 +117,9 @@ public class ProxyHostnameV2DistTest {
}
private void assertXForwardedHeaders() {
- given().header("X-Forwarded-Host", "test").when().get("http://mykeycloak.org:8080").then().header(HttpHeaders.LOCATION, containsString("http://test:8080/admin"));
- given().header("X-Forwarded-Host", "test").when().get("http://localhost:8080").then().header(HttpHeaders.LOCATION, containsString("http://test:8080/admin"));
- given().header("X-Forwarded-Host", "test").when().get("https://localhost:8443").then().header(HttpHeaders.LOCATION, containsString("https://test:8443/admin"));
+ given().header("X-Forwarded-Host", "test:123").when().get("http://mykeycloak.org:8080").then().header(HttpHeaders.LOCATION, containsString("http://test:123/admin"));
+ given().header("X-Forwarded-Host", "test:123").when().get("http://localhost:8080").then().header(HttpHeaders.LOCATION, containsString("http://test:123/admin"));
+ given().header("X-Forwarded-Host", "test:123").when().get("https://localhost:8443").then().header(HttpHeaders.LOCATION, containsString("https://test:123/admin"));
given().header("X-Forwarded-Proto", "https").when().get("http://localhost:8080").then().header(HttpHeaders.LOCATION, containsString("https://localhost/admin"));
given().header("X-Forwarded-Proto", "https").header("X-Forwarded-Port", "8443").when().get("http://localhost:8080").then().header(HttpHeaders.LOCATION, containsString("https://localhost:8443/admin"));
}
@@ -140,4 +140,4 @@ public class ProxyHostnameV2DistTest {
Assert.assertEquals(expectedBaseUrl + "realms/master/protocol/openid-connect/auth", getServerMetadata(requestBaseUrl)
.getAuthorizationEndpoint());
}
-}
\ No newline at end of file
+}
diff --git a/test-framework/core/src/main/java/org/keycloak/testframework/LogHandler.java b/test-framework/core/src/main/java/org/keycloak/testframework/LogHandler.java
index 4336b2c6cdd..0cfe91f3709 100644
--- a/test-framework/core/src/main/java/org/keycloak/testframework/LogHandler.java
+++ b/test-framework/core/src/main/java/org/keycloak/testframework/LogHandler.java
@@ -1,6 +1,7 @@
package org.keycloak.testframework;
import io.quarkus.runtime.logging.LoggingSetupRecorder;
+import io.smallrye.config.SmallRyeConfigProviderResolver;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.jboss.logging.Logger;
import org.jboss.logmanager.LogManager;
@@ -26,7 +27,14 @@ public class LogHandler {
}
private static void initializeQuarkusLogging() {
- ConfigProviderResolver.instance().registerConfig(Config.getConfig(), Thread.currentThread().getContextClassLoader());
+ // We do not care about Config that was created by Quarkus' TestConfigProviderResolver.
+ // Alternatively, a Customizer could be used so we could keep the Config created by Quarkus but this is not Quarkus tests,
+ // relying on Quarkus' Config is not necessary and might be fragile.
+ SmallRyeConfigProviderResolver configProviderResolver = (SmallRyeConfigProviderResolver) ConfigProviderResolver.instance();
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+
+ configProviderResolver.releaseConfig(cl);
+ ConfigProviderResolver.instance().registerConfig(Config.getConfig(), cl);
LoggingSetupRecorder.handleFailedStart();
}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserPropertyFileStorage.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserPropertyFileStorage.java
index 604a78190be..7310081a5a2 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserPropertyFileStorage.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/UserPropertyFileStorage.java
@@ -111,7 +111,8 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP
public int getUsersCount(RealmModel realm, Map params) {
addCall(COUNT_SEARCH_METHOD);
- return (int) searchForUser(realm, params.get(UserModel.SEARCH), null, null, username -> username.contains(params.get(UserModel.SEARCH))).count();
+ String search = params.get(UserModel.SEARCH);
+ return (int) searchForUser(realm, search, null, null, username -> search == null || username.contains(search)).count();
}
@Override