/* * Copyright Strimzi authors. * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). */ package io.strimzi.operator.topic; import io.fabric8.kubernetes.api.model.Event; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.dsl.MixedOperation; import io.fabric8.kubernetes.client.dsl.Resource; import io.strimzi.api.kafka.Crds; import io.strimzi.api.kafka.KafkaTopicList; import io.strimzi.api.kafka.model.DoneableKafkaTopic; import io.strimzi.api.kafka.model.KafkaTopic; import io.strimzi.operator.common.Util; import io.strimzi.operator.common.operator.resource.CrdOperator; import io.vertx.core.Future; import io.vertx.core.Promise; import io.vertx.core.Vertx; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.List; public class K8sImpl implements K8s { private final static Logger LOGGER = LogManager.getLogger(K8sImpl.class); private final Labels labels; private final String namespace; private final KubernetesClient client; private final CrdOperator<KubernetesClient, KafkaTopic, KafkaTopicList, DoneableKafkaTopic> crdOperator; private final Vertx vertx; public K8sImpl(Vertx vertx, KubernetesClient client, Labels labels, String namespace) { this.vertx = vertx; this.client = client; this.crdOperator = new CrdOperator<>(vertx, client, KafkaTopic.class, KafkaTopicList.class, DoneableKafkaTopic.class, Crds.kafkaTopic()); this.labels = labels; this.namespace = namespace; } @Override public Future<KafkaTopic> createResource(KafkaTopic topicResource) { Promise<KafkaTopic> handler = Promise.promise(); vertx.executeBlocking(future -> { try { KafkaTopic kafkaTopic = operation().inNamespace(namespace).create(topicResource); LOGGER.debug("KafkaTopic {} created with version {}->{}", kafkaTopic.getMetadata().getName(), topicResource.getMetadata() != null ? topicResource.getMetadata().getResourceVersion() : null, kafkaTopic.getMetadata().getResourceVersion()); future.complete(kafkaTopic); } catch (Exception e) { future.fail(e); } }, handler); return handler.future(); } @Override public Future<KafkaTopic> updateResource(KafkaTopic topicResource) { Promise<KafkaTopic> handler = Promise.promise(); vertx.executeBlocking(future -> { try { KafkaTopic kafkaTopic = operation().inNamespace(namespace).withName(topicResource.getMetadata().getName()).patch(topicResource); LOGGER.debug("KafkaTopic {} updated with version {}->{}", kafkaTopic != null && kafkaTopic.getMetadata() != null ? kafkaTopic.getMetadata().getName() : null, topicResource.getMetadata() != null ? topicResource.getMetadata().getResourceVersion() : null, kafkaTopic != null && kafkaTopic.getMetadata() != null ? kafkaTopic.getMetadata().getResourceVersion() : null); future.complete(kafkaTopic); } catch (Exception e) { future.fail(e); } }, handler); return handler.future(); } @Override public Future<KafkaTopic> updateResourceStatus(KafkaTopic topicResource) { return crdOperator.updateStatusAsync(topicResource); } @Override public Future<Void> deleteResource(ResourceName resourceName) { Promise<Void> handler = Promise.promise(); vertx.executeBlocking(future -> { try { // Delete the resource by the topic name, because neither ZK nor Kafka know the resource name if (!Boolean.TRUE.equals(operation().inNamespace(namespace).withName(resourceName.toString()).cascading(true).delete())) { LOGGER.warn("KafkaTopic {} could not be deleted, since it doesn't seem to exist", resourceName.toString()); future.complete(); } else { Util.waitFor(vertx, "sync resource deletion " + resourceName, "deleted", 1000, Long.MAX_VALUE, () -> { KafkaTopic kafkaTopic = operation().inNamespace(namespace).withName(resourceName.toString()).get(); boolean notExists = kafkaTopic == null; LOGGER.debug("KafkaTopic {} deleted {}", resourceName.toString(), notExists); return notExists; }).onComplete(future); } } catch (Exception e) { future.fail(e); } }, handler); return handler.future(); } private MixedOperation<KafkaTopic, KafkaTopicList, DoneableKafkaTopic, Resource<KafkaTopic, DoneableKafkaTopic>> operation() { return client.customResources(Crds.kafkaTopic(), KafkaTopic.class, KafkaTopicList.class, DoneableKafkaTopic.class); } @Override public Future<List<KafkaTopic>> listResources() { return crdOperator.listAsync(namespace, io.strimzi.operator.common.model.Labels.fromMap(labels.labels())); } @Override public Future<KafkaTopic> getFromName(ResourceName resourceName) { return crdOperator.getAsync(namespace, resourceName.toString()); } /** * Create the given k8s event */ @Override public Future<Void> createEvent(Event event) { Promise<Void> handler = Promise.promise(); vertx.executeBlocking(future -> { try { try { LOGGER.debug("Creating event {}", event); client.events().inNamespace(namespace).create(event); } catch (KubernetesClientException e) { LOGGER.error("Error creating event {}", event, e); } future.complete(); } catch (Exception e) { future.fail(e); } }, handler); return handler.future(); } }