use of io.fabric8.kubernetes.api.model.Condition in project strimzi by strimzi.
the class ResourceSupport method selfClosingWatch.
/**
* Watches the given {@code watchable} using the given
* {@code watchFn},
* returning a Future which completes when {@code watchFn} returns non-null
* to some event on the watchable, or after a timeout.
*
* The given {@code watchFn} will be invoked on a worker thread when the
* Kubernetes resources changes, so may block.
* When the {@code watchFn} returns non-null the watch will be closed and then
* the future returned from this method will be completed on the context thread.
*
* In some cases such as resource deletion, it might happen that the resource is deleted already before the watch is
* started and as a result the watch never completes. The {@code preCheckFn} will be invoked on a worker thread
* after the watch has been created. It is expected to double check if we still need to wait for the watch to fire.
* When the {@code preCheckFn} returns non-null the watch will be closed and the future returned from this method
* will be completed with the result of the {@code preCheckFn} on the context thread. In the deletion example
* described above, the {@code preCheckFn} can check if the resource still exists and close the watch in case it was
* already deleted.
*
* @param reconciliation Reconciliation marker used for logging
* @param watchable The watchable - used to watch the resource.
* @param gettable The Gettable - used to get the resource in the pre-check.
* @param operationTimeoutMs The timeout in ms.
* @param watchFnDescription A description of what {@code watchFn} is watching for.
* E.g. "observe ${condition} of ${kind} ${namespace}/${name}".
* @param watchFn The function to determine if the event occured
* @param preCheckFn Pre-check function to avoid situation when the watch is never fired because ot was started too late.
* @param <T> The type of watched resource.
* @param <U> The result type of the {@code watchFn}.
*
* @return A Futures which completes when the {@code watchFn} returns non-null
* in response to some Kubenetes even on the watched resource(s).
*/
<T, U> Future<U> selfClosingWatch(Reconciliation reconciliation, Watchable<Watcher<T>> watchable, Gettable<T> gettable, long operationTimeoutMs, String watchFnDescription, BiFunction<Watcher.Action, T, U> watchFn, Function<T, U> preCheckFn) {
return new Watcher<T>() {
private final Promise<Watch> watchPromise;
private final Promise<U> donePromise;
private final Promise<U> resultPromise;
private final long timerId;
/* init */
{
this.watchPromise = Promise.promise();
this.donePromise = Promise.promise();
this.resultPromise = Promise.promise();
this.timerId = vertx.setTimer(operationTimeoutMs, ignored -> donePromise.tryFail(new TimeoutException("\"" + watchFnDescription + "\" timed out after " + operationTimeoutMs + "ms")));
CompositeFuture.join(watchPromise.future(), donePromise.future()).onComplete(joinResult -> {
Future<Void> closeFuture;
if (watchPromise.future().succeeded()) {
closeFuture = closeOnWorkerThread(watchPromise.future().result());
} else {
closeFuture = Future.succeededFuture();
}
closeFuture.onComplete(closeResult -> vertx.runOnContext(ignored2 -> {
LOGGER.debugCr(reconciliation, "Completing watch future");
if (joinResult.succeeded() && closeResult.succeeded()) {
resultPromise.complete(joinResult.result().resultAt(1));
} else {
resultPromise.fail(collectCauses(joinResult, closeResult));
}
}));
});
try {
Watch watch = watchable.watch(this);
LOGGER.debugCr(reconciliation, "Opened watch {} for evaluation of {}", watch, watchFnDescription);
// Pre-check is done after the watch is open to make sure we did not missed the event. In the worst
// case, both pre-check and watch complete the future. But at least one should always complete it.
U apply = preCheckFn.apply(gettable.get());
if (apply != null) {
LOGGER.debugCr(reconciliation, "Pre-check is already complete, no need to wait for the watch: {}", watchFnDescription);
donePromise.tryComplete(apply);
vertx.cancelTimer(timerId);
} else {
LOGGER.debugCr(reconciliation, "Pre-check is not complete yet, let's wait for the watch: {}", watchFnDescription);
}
watchPromise.complete(watch);
} catch (Throwable t) {
watchPromise.fail(t);
}
}
@Override
public void eventReceived(Action action, T resource) {
vertx.executeBlocking(f -> {
try {
U apply = watchFn.apply(action, resource);
if (apply != null) {
LOGGER.debugCr(reconciliation, "Satisfied: {}", watchFnDescription);
f.tryComplete(apply);
vertx.cancelTimer(timerId);
} else {
LOGGER.debugCr(reconciliation, "Not yet satisfied: {}", watchFnDescription);
}
} catch (Throwable t) {
if (!f.tryFail(t)) {
LOGGER.debugCr(reconciliation, "Ignoring exception thrown while " + "evaluating watch {} because the future was already completed", watchFnDescription, t);
}
}
}, true, donePromise);
}
@Override
public void onClose(WatcherException cause) {
}
}.resultPromise.future();
}
use of io.fabric8.kubernetes.api.model.Condition in project strimzi by strimzi.
the class LoggingChangeST method testNotExistingCMSetsDefaultLogging.
@ParallelNamespaceTest
void testNotExistingCMSetsDefaultLogging(ExtensionContext extensionContext) {
final String namespaceName = StUtils.getNamespaceBasedOnRbac(namespace, extensionContext);
final String defaultProps = TestUtils.getFileAsString(TestUtils.USER_PATH + "/../cluster-operator/src/main/resources/kafkaDefaultLoggingProperties");
final String clusterName = mapWithClusterNames.get(extensionContext.getDisplayName());
final LabelSelector kafkaSelector = KafkaResource.getLabelSelector(clusterName, KafkaResources.kafkaStatefulSetName(clusterName));
String cmData = "log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender\n" + "log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout\n" + "log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %p %m (%c) [%t]%n\n" + "log4j.rootLogger=INFO, CONSOLE\n" + "log4j.logger.org.I0Itec.zkclient.ZkClient=INFO\n" + "log4j.logger.org.apache.zookeeper=INFO\n" + "log4j.logger.kafka=INFO\n" + "log4j.logger.org.apache.kafka=INFO";
String existingCmName = "external-cm";
String nonExistingCmName = "non-existing-cm-name";
ConfigMap configMap = new ConfigMapBuilder().withNewMetadata().withName(existingCmName).withNamespace(namespaceName).endMetadata().withData(Collections.singletonMap("log4j.properties", cmData)).build();
kubeClient().getClient().configMaps().inNamespace(namespaceName).createOrReplace(configMap);
LOGGER.info("Deploying Kafka with custom logging");
resourceManager.createResource(extensionContext, KafkaTemplates.kafkaPersistent(clusterName, 3, 1).editOrNewSpec().editKafka().withExternalLogging(new ExternalLoggingBuilder().withNewValueFrom().withConfigMapKeyRef(new ConfigMapKeySelectorBuilder().withKey("log4j.properties").withName(existingCmName).withOptional(false).build()).endValueFrom().build()).endKafka().endSpec().build());
String kafkaSsName = KafkaResources.kafkaStatefulSetName(clusterName);
Map<String, String> kafkaPods = PodUtils.podSnapshot(namespaceName, kafkaSelector);
String log4jFile = cmdKubeClient().namespace(namespaceName).execInPodContainer(Level.DEBUG, KafkaResources.kafkaPodName(clusterName, 0), "kafka", "/bin/bash", "-c", "cat custom-config/log4j.properties").out();
assertTrue(log4jFile.contains(cmData));
LOGGER.info("Changing external logging's CM to not existing one");
KafkaResource.replaceKafkaResourceInSpecificNamespace(clusterName, kafka -> kafka.getSpec().getKafka().setLogging(new ExternalLoggingBuilder().withNewValueFrom().withConfigMapKeyRef(new ConfigMapKeySelectorBuilder().withKey("log4j.properties").withName(nonExistingCmName).withOptional(false).build()).endValueFrom().build()), namespaceName);
RollingUpdateUtils.waitForNoRollingUpdate(namespaceName, kafkaSelector, kafkaPods);
LOGGER.info("Checking that log4j.properties in custom-config isn't empty and configuration is default");
log4jFile = cmdKubeClient().namespace(namespaceName).execInPodContainer(Level.DEBUG, KafkaResources.kafkaPodName(clusterName, 0), "kafka", "/bin/bash", "-c", "cat custom-config/log4j.properties").out();
assertFalse(log4jFile.isEmpty());
assertTrue(log4jFile.contains(cmData));
assertFalse(log4jFile.contains(defaultProps));
LOGGER.info("Checking if Kafka:{} contains error about non-existing CM", clusterName);
Condition condition = KafkaResource.kafkaClient().inNamespace(namespaceName).withName(clusterName).get().getStatus().getConditions().get(0);
assertThat(condition.getType(), is(CustomResourceStatus.NotReady.toString()));
assertTrue(condition.getMessage().matches("ConfigMap " + nonExistingCmName + " with external logging configuration does not exist .*"));
}
use of io.fabric8.kubernetes.api.model.Condition in project strimzi by strimzi.
the class AllNamespaceIsolatedST method testUserInDifferentNamespace.
@IsolatedTest
void testUserInDifferentNamespace(ExtensionContext extensionContext) {
String startingNamespace = cluster.setNamespace(SECOND_NAMESPACE);
KafkaUser user = KafkaUserTemplates.tlsUser(MAIN_NAMESPACE_CLUSTER_NAME, USER_NAME).build();
resourceManager.createResource(extensionContext, user);
Condition kafkaCondition = KafkaUserResource.kafkaUserClient().inNamespace(SECOND_NAMESPACE).withName(USER_NAME).get().getStatus().getConditions().get(0);
LOGGER.info("KafkaUser condition status: {}", kafkaCondition.getStatus());
LOGGER.info("KafkaUser condition type: {}", kafkaCondition.getType());
assertThat(kafkaCondition.getType(), is(Ready.toString()));
List<Secret> secretsOfSecondNamespace = kubeClient(SECOND_NAMESPACE).listSecrets();
cluster.setNamespace(THIRD_NAMESPACE);
for (Secret s : secretsOfSecondNamespace) {
if (s.getMetadata().getName().equals(USER_NAME)) {
LOGGER.info("Copying secret {} from namespace {} to namespace {}", s, SECOND_NAMESPACE, THIRD_NAMESPACE);
copySecret(s, THIRD_NAMESPACE, USER_NAME);
}
}
resourceManager.createResource(extensionContext, KafkaClientsTemplates.kafkaClients(true, MAIN_NAMESPACE_CLUSTER_NAME + "-" + Constants.KAFKA_CLIENTS, user).build());
final String defaultKafkaClientsPodName = ResourceManager.kubeClient().listPodsByPrefixInName(MAIN_NAMESPACE_CLUSTER_NAME + "-" + Constants.KAFKA_CLIENTS).get(0).getMetadata().getName();
InternalKafkaClient internalKafkaClient = new InternalKafkaClient.Builder().withUsingPodName(defaultKafkaClientsPodName).withTopicName(TOPIC_NAME).withNamespaceName(THIRD_NAMESPACE).withClusterName(MAIN_NAMESPACE_CLUSTER_NAME).withMessageCount(MESSAGE_COUNT).withKafkaUsername(USER_NAME).withListenerName(Constants.TLS_LISTENER_DEFAULT_NAME).build();
LOGGER.info("Checking produced and consumed messages to pod:{}", defaultKafkaClientsPodName);
int sent = internalKafkaClient.sendMessagesTls();
assertThat(sent, is(MESSAGE_COUNT));
int received = internalKafkaClient.receiveMessagesTls();
assertThat(received, is(MESSAGE_COUNT));
cluster.setNamespace(startingNamespace);
}
use of io.fabric8.kubernetes.api.model.Condition in project strimzi by strimzi.
the class KafkaMirrorMaker2AssemblyOperator method createOrUpdate.
@Override
protected Future<KafkaMirrorMaker2Status> createOrUpdate(Reconciliation reconciliation, KafkaMirrorMaker2 kafkaMirrorMaker2) {
KafkaMirrorMaker2Cluster mirrorMaker2Cluster;
KafkaMirrorMaker2Status kafkaMirrorMaker2Status = new KafkaMirrorMaker2Status();
try {
mirrorMaker2Cluster = KafkaMirrorMaker2Cluster.fromCrd(reconciliation, kafkaMirrorMaker2, versions);
} catch (Exception e) {
LOGGER.warnCr(reconciliation, e);
StatusUtils.setStatusConditionAndObservedGeneration(kafkaMirrorMaker2, kafkaMirrorMaker2Status, Future.failedFuture(e));
return Future.failedFuture(new ReconciliationException(kafkaMirrorMaker2Status, e));
}
Promise<KafkaMirrorMaker2Status> createOrUpdatePromise = Promise.promise();
String namespace = reconciliation.namespace();
Map<String, String> annotations = new HashMap<>(1);
final AtomicReference<String> desiredLogging = new AtomicReference<>();
boolean mirrorMaker2HasZeroReplicas = mirrorMaker2Cluster.getReplicas() == 0;
LOGGER.debugCr(reconciliation, "Updating Kafka MirrorMaker 2.0 cluster");
connectServiceAccount(reconciliation, namespace, KafkaMirrorMaker2Resources.serviceAccountName(mirrorMaker2Cluster.getCluster()), mirrorMaker2Cluster).compose(i -> connectNetworkPolicy(reconciliation, namespace, mirrorMaker2Cluster, true)).compose(i -> deploymentOperations.scaleDown(reconciliation, namespace, mirrorMaker2Cluster.getName(), mirrorMaker2Cluster.getReplicas())).compose(i -> serviceOperations.reconcile(reconciliation, namespace, mirrorMaker2Cluster.getServiceName(), mirrorMaker2Cluster.generateService())).compose(i -> generateMetricsAndLoggingConfigMap(reconciliation, namespace, mirrorMaker2Cluster)).compose(logAndMetricsConfigMap -> {
String logging = logAndMetricsConfigMap.getData().get(AbstractModel.ANCILLARY_CM_KEY_LOG_CONFIG);
annotations.put(Annotations.ANNO_STRIMZI_LOGGING_DYNAMICALLY_UNCHANGEABLE_HASH, Util.stringHash(Util.getLoggingDynamicallyUnmodifiableEntries(logging)));
desiredLogging.set(logging);
return configMapOperations.reconcile(reconciliation, namespace, mirrorMaker2Cluster.getAncillaryConfigMapName(), logAndMetricsConfigMap);
}).compose(i -> kafkaConnectJmxSecret(reconciliation, namespace, mirrorMaker2Cluster.getName(), mirrorMaker2Cluster)).compose(i -> pfa.hasPodDisruptionBudgetV1() ? podDisruptionBudgetOperator.reconcile(reconciliation, namespace, mirrorMaker2Cluster.getName(), mirrorMaker2Cluster.generatePodDisruptionBudget()) : Future.succeededFuture()).compose(i -> !pfa.hasPodDisruptionBudgetV1() ? podDisruptionBudgetV1Beta1Operator.reconcile(reconciliation, namespace, mirrorMaker2Cluster.getName(), mirrorMaker2Cluster.generatePodDisruptionBudgetV1Beta1()) : Future.succeededFuture()).compose(i -> generateAuthHash(namespace, kafkaMirrorMaker2.getSpec())).compose(hash -> {
if (hash != null) {
annotations.put(Annotations.ANNO_STRIMZI_AUTH_HASH, Integer.toString(hash));
}
Deployment deployment = mirrorMaker2Cluster.generateDeployment(annotations, pfa.isOpenshift(), imagePullPolicy, imagePullSecrets);
return deploymentOperations.reconcile(reconciliation, namespace, mirrorMaker2Cluster.getName(), deployment);
}).compose(i -> deploymentOperations.scaleUp(reconciliation, namespace, mirrorMaker2Cluster.getName(), mirrorMaker2Cluster.getReplicas())).compose(i -> deploymentOperations.waitForObserved(reconciliation, namespace, mirrorMaker2Cluster.getName(), 1_000, operationTimeoutMs)).compose(i -> mirrorMaker2HasZeroReplicas ? Future.succeededFuture() : deploymentOperations.readiness(reconciliation, namespace, mirrorMaker2Cluster.getName(), 1_000, operationTimeoutMs)).compose(i -> mirrorMaker2HasZeroReplicas ? Future.succeededFuture() : reconcileConnectors(reconciliation, kafkaMirrorMaker2, mirrorMaker2Cluster, kafkaMirrorMaker2Status, desiredLogging.get())).map((Void) null).onComplete(reconciliationResult -> {
List<Condition> conditions = kafkaMirrorMaker2Status.getConditions();
StatusUtils.setStatusConditionAndObservedGeneration(kafkaMirrorMaker2, kafkaMirrorMaker2Status, reconciliationResult);
if (!mirrorMaker2HasZeroReplicas) {
kafkaMirrorMaker2Status.setUrl(KafkaMirrorMaker2Resources.url(mirrorMaker2Cluster.getCluster(), namespace, KafkaMirrorMaker2Cluster.REST_API_PORT));
}
if (conditions != null && !conditions.isEmpty()) {
kafkaMirrorMaker2Status.addConditions(conditions);
}
kafkaMirrorMaker2Status.setReplicas(mirrorMaker2Cluster.getReplicas());
kafkaMirrorMaker2Status.setLabelSelector(mirrorMaker2Cluster.getSelectorLabels().toSelectorString());
if (reconciliationResult.succeeded()) {
createOrUpdatePromise.complete(kafkaMirrorMaker2Status);
} else {
createOrUpdatePromise.fail(new ReconciliationException(kafkaMirrorMaker2Status, reconciliationResult.cause()));
}
});
return createOrUpdatePromise.future();
}
use of io.fabric8.kubernetes.api.model.Condition in project hugegraph-computer by hugegraph.
the class KubeUtil method waitUntilReady.
/**
* Tries a condition func until the initial delay specified.
*
* @param initialDelay the initial delay
* @param interval the check interval
* @param timeout the timeout period
* @param condition the condition
* @param executor the executor
* @return returns true if gracefully finished
*/
public static boolean waitUntilReady(Duration initialDelay, Duration interval, Duration timeout, Supplier<Boolean> condition, ScheduledExecutorService executor) {
AtomicBoolean result = new AtomicBoolean(false);
long deadline = System.currentTimeMillis() + timeout.toMillis();
ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> {
try {
result.set(condition.get());
} catch (Exception e) {
result.set(false);
}
}, initialDelay.toMillis(), interval.toMillis(), TimeUnit.MILLISECONDS);
try {
while (System.currentTimeMillis() < deadline) {
if (result.get()) {
return true;
}
}
} finally {
future.cancel(true);
}
return result.get();
}
Aggregations