use of io.strimzi.api.kafka.model.KafkaTopic in project strimzi-kafka-operator by strimzi.
the class TopicOperatorMockTest method testNotCreatedInKube.
void testNotCreatedInKube(VertxTestContext context, KafkaTopic kt) throws InterruptedException, ExecutionException {
String kubeName = kt.getMetadata().getName();
String kafkaName = kt.getSpec().getTopicName() != null ? kt.getSpec().getTopicName() : kubeName;
int retention = (Integer) kt.getSpec().getConfig().get("retention.bytes");
createInKube(kt);
Thread.sleep(2000);
LOGGER.info("Topic has not been created");
Topic fromKafka = getFromKafka(kafkaName);
context.verify(() -> assertThat(fromKafka, is(nullValue())));
// Reconcile after no changes
reconcile(context);
// Check things still the same
context.verify(() -> assertThat(fromKafka, is(nullValue())));
// Config change + reconcile
updateInKube(new KafkaTopicBuilder(kt).editSpec().addToConfig("retention.bytes", retention + 1).endSpec().build());
// Another reconciliation
reconcile(context);
// Check things still the same
context.verify(() -> {
assertThat(getFromKafka(kafkaName), is(nullValue()));
context.completeNow();
});
}
use of io.strimzi.api.kafka.model.KafkaTopic in project strimzi-kafka-operator by strimzi.
the class TopicOperatorMockTest method testCreatedWithSameTopicNameInKube.
@Test
public void testCreatedWithSameTopicNameInKube(VertxTestContext context) throws InterruptedException, ExecutionException {
int retention = 100_000_000;
KafkaTopic kt = new KafkaTopicBuilder().withNewMetadata().withName("my-topic").withNamespace(NAMESPACE).addToLabels(Labels.STRIMZI_KIND_LABEL, "topic").endMetadata().withNewSpec().withTopicName(// the same as metadata.name
"my-topic").withPartitions(1).withReplicas(1).addToConfig("retention.bytes", retention).endSpec().build();
testCreatedInKube(context, kt);
}
use of io.strimzi.api.kafka.model.KafkaTopic in project strimzi-kafka-operator by strimzi.
the class TopicOperatorMockTest method testCreatedWithoutTopicNameInKube.
@Test
public void testCreatedWithoutTopicNameInKube(VertxTestContext context) throws InterruptedException, ExecutionException {
LOGGER.info("Test started");
int retention = 100_000_000;
KafkaTopic kt = new KafkaTopicBuilder().withNewMetadata().withName("my-topic").withNamespace(NAMESPACE).addToLabels(Labels.STRIMZI_KIND_LABEL, "topic").addToLabels(Labels.KUBERNETES_NAME_LABEL, "topic-operator").endMetadata().withNewSpec().withPartitions(1).withReplicas(1).addToConfig("retention.bytes", retention).endSpec().build();
testCreatedInKube(context, kt);
}
use of io.strimzi.api.kafka.model.KafkaTopic in project strimzi-kafka-operator by strimzi.
the class MockK8s method updateResource.
@Override
public Future<KafkaTopic> updateResource(KafkaTopic topicResource) {
Promise<KafkaTopic> handler = Promise.promise();
AsyncResult<Void> response = modifyResponse.apply(new ResourceName(topicResource));
if (response.succeeded()) {
AsyncResult<KafkaTopic> old = byName.put(new ResourceName(topicResource), Future.succeededFuture(topicResource));
if (old == null) {
handler.handle(Future.failedFuture("resource does not exist, cannot be updated: " + topicResource.getMetadata().getName()));
return handler.future();
}
}
if (response.succeeded()) {
Long generation = topicResource.getMetadata().getGeneration();
handler.complete(new KafkaTopicBuilder(topicResource).editMetadata().withGeneration(generation != null ? generation + 1 : 1).endMetadata().build());
} else {
handler.fail(response.cause());
}
return handler.future();
}
use of io.strimzi.api.kafka.model.KafkaTopic in project strimzi-kafka-operator by strimzi.
the class TopicOperator method update3Way.
private Future<Void> update3Way(Reconciliation reconciliation, LogContext logContext, HasMetadata involvedObject, Topic k8sTopic, Topic kafkaTopic, Topic privateTopic) {
final Future<Void> reconciliationResultHandler;
if (!privateTopic.getResourceName().equals(k8sTopic.getResourceName())) {
return Future.failedFuture(new OperatorException(involvedObject, "Topic '" + kafkaTopic.getTopicName() + "' is already managed via KafkaTopic '" + privateTopic.getResourceName() + "' it cannot also be managed via the KafkaTopic '" + k8sTopic.getResourceName() + "'"));
}
TopicDiff oursKafka = TopicDiff.diff(privateTopic, kafkaTopic);
LOGGER.debugCr(logContext.toReconciliation(), "topicStore->kafkaTopic: {}", oursKafka);
TopicDiff oursK8s = TopicDiff.diff(privateTopic, k8sTopic);
LOGGER.debugCr(logContext.toReconciliation(), "topicStore->k8sTopic: {}", oursK8s);
String conflict = oursKafka.conflict(oursK8s);
if (conflict != null) {
final String message = "KafkaTopic resource and Kafka topic both changed in a conflicting way: " + conflict;
LOGGER.errorCr(logContext.toReconciliation(), "{}", message);
enqueue(logContext, new Event(logContext, involvedObject, message, EventType.INFO, eventResult -> {
}));
reconciliationResultHandler = Future.failedFuture(new ConflictingChangesException(involvedObject, message));
} else {
TopicDiff merged = oursKafka.merge(oursK8s);
LOGGER.debugCr(logContext.toReconciliation(), "Diffs do not conflict, merged diff: {}", merged);
if (merged.isEmpty()) {
LOGGER.infoCr(logContext.toReconciliation(), "All three topics are identical");
reconciliationResultHandler = Future.succeededFuture();
} else {
Topic result = merged.apply(privateTopic);
int partitionsDelta = merged.numPartitionsDelta();
if (partitionsDelta < 0) {
final String message = "Number of partitions cannot be decreased";
LOGGER.errorCr(logContext.toReconciliation(), "{}", message);
enqueue(logContext, new Event(logContext, involvedObject, message, EventType.INFO, eventResult -> {
}));
reconciliationResultHandler = Future.failedFuture(new PartitionDecreaseException(involvedObject, message));
} else if (oursK8s.changesReplicationFactor() && !oursKafka.changesReplicationFactor()) {
reconciliationResultHandler = Future.failedFuture(new ReplicationFactorChangeException(involvedObject, "Changing 'spec.replicas' is not supported. " + "This KafkaTopic's 'spec.replicas' should be reverted to " + kafkaTopic.getNumReplicas() + " and then the replication should be changed directly in Kafka."));
} else {
// TODO What if we increase min.in.sync.replicas and the number of replicas,
// such that the old number of replicas < the new min isr? But likewise
// we could decrease, so order of tasks in the queue will need to change
// depending on what the diffs are.
LOGGER.debugCr(logContext.toReconciliation(), "Updating KafkaTopic, kafka topic and topicStore");
TopicDiff kubeDiff = TopicDiff.diff(k8sTopic, result);
reconciliationResultHandler = Future.succeededFuture().compose(updatedKafkaTopic -> {
Future<Void> configFuture;
TopicDiff kafkaDiff = TopicDiff.diff(kafkaTopic, result);
if (merged.changesConfig() && !kafkaDiff.isEmpty()) {
Promise<Void> promise = Promise.promise();
configFuture = promise.future();
LOGGER.debugCr(logContext.toReconciliation(), "Updating kafka config with {}", kafkaDiff);
enqueue(logContext, new UpdateKafkaConfig(logContext, result, involvedObject, promise));
} else {
LOGGER.debugCr(logContext.toReconciliation(), "No need to update kafka topic with {}", kafkaDiff);
configFuture = Future.succeededFuture();
}
return configFuture;
}).compose(ignored -> {
Future<KafkaTopic> resourceFuture;
if (!kubeDiff.isEmpty()) {
LOGGER.debugCr(logContext.toReconciliation(), "Updating KafkaTopic with {}", kubeDiff);
resourceFuture = updateResource(logContext, result).map(updatedKafkaTopic -> {
reconciliation.observedTopicFuture(updatedKafkaTopic);
return updatedKafkaTopic;
});
} else {
LOGGER.debugCr(logContext.toReconciliation(), "No need to update KafkaTopic {}", kubeDiff);
resourceFuture = Future.succeededFuture();
}
return resourceFuture;
}).compose(ignored -> {
if (partitionsDelta > 0 && // Kafka throws an error if we attempt a noop change #partitions
result.getNumPartitions() > kafkaTopic.getNumPartitions()) {
Promise<Void> partitionsPromise = Promise.promise();
enqueue(logContext, new IncreaseKafkaPartitions(logContext, result, involvedObject, partitionsPromise));
return partitionsPromise.future();
} else {
return Future.succeededFuture();
}
}).compose(ignored -> {
Promise<Void> topicStorePromise = Promise.promise();
enqueue(logContext, new UpdateInTopicStore(logContext, result, involvedObject, topicStorePromise));
return topicStorePromise.future();
});
}
}
}
return reconciliationResultHandler;
}
Aggregations