/* * Copyright Strimzi authors. * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). */ package io.strimzi.systemtest.rollingupdate; import io.fabric8.kubernetes.api.model.Event; import io.strimzi.api.kafka.model.KafkaResources; import io.strimzi.operator.common.Annotations; import io.strimzi.systemtest.BaseST; import io.strimzi.systemtest.Constants; import io.strimzi.systemtest.resources.ResourceManager; import io.strimzi.systemtest.resources.crd.KafkaResource; import io.strimzi.systemtest.resources.crd.KafkaTopicResource; import io.strimzi.systemtest.utils.kafkaUtils.KafkaTopicUtils; import io.strimzi.systemtest.utils.kubeUtils.controllers.StatefulSetUtils; import io.strimzi.systemtest.utils.kubeUtils.objects.PodUtils; import io.strimzi.test.timemeasuring.Operation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import java.util.List; import java.util.Map; import static io.strimzi.systemtest.Constants.INTERNAL_CLIENTS_USED; import static io.strimzi.systemtest.Constants.REGRESSION; import static io.strimzi.systemtest.k8s.Events.Created; import static io.strimzi.systemtest.k8s.Events.Pulled; import static io.strimzi.systemtest.k8s.Events.Scheduled; import static io.strimzi.systemtest.k8s.Events.Started; import static io.strimzi.systemtest.matchers.Matchers.hasAllOfReasons; import static io.strimzi.test.k8s.KubeClusterResource.kubeClient; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; @Tag(REGRESSION) @Tag(INTERNAL_CLIENTS_USED) class KafkaRollerST extends BaseST { private static final Logger LOGGER = LogManager.getLogger(RollingUpdateST.class); static final String NAMESPACE = "kafka-roller-cluster-test"; @Test void testKafkaRollsWhenTopicIsUnderReplicated() { String topicName = KafkaTopicUtils.generateRandomNameOfTopic(); timeMeasuringSystem.setOperationID(timeMeasuringSystem.startTimeMeasuring(Operation.CLUSTER_RECOVERY)); KafkaResource.kafkaPersistent(CLUSTER_NAME, 4) .editSpec() .editKafka() .addToConfig("auto.create.topics.enable", "false") .endKafka() .endSpec() .done(); LOGGER.info("Running kafkaScaleUpScaleDown {}", CLUSTER_NAME); final int initialReplicas = kubeClient().getStatefulSet(KafkaResources.kafkaStatefulSetName(CLUSTER_NAME)).getStatus().getReplicas(); assertEquals(4, initialReplicas); Map<String, String> kafkaPods = StatefulSetUtils.ssSnapshot(KafkaResources.kafkaStatefulSetName(CLUSTER_NAME)); KafkaTopicResource.topic(CLUSTER_NAME, topicName, 4, 4, 4).done(); //Test that the new pod does not have errors or failures in events String uid = kubeClient().getPodUid(KafkaResources.kafkaPodName(CLUSTER_NAME, 3)); List<Event> events = kubeClient().listEvents(uid); assertThat(events, hasAllOfReasons(Scheduled, Pulled, Created, Started)); //Test that CO doesn't have any exceptions in log timeMeasuringSystem.stopOperation(timeMeasuringSystem.getOperationID()); assertNoCoErrorsLogged(timeMeasuringSystem.getDurationInSeconds(testClass, testName, timeMeasuringSystem.getOperationID())); // scale down int scaledDownReplicas = 3; LOGGER.info("Scaling down to {}", scaledDownReplicas); timeMeasuringSystem.setOperationID(timeMeasuringSystem.startTimeMeasuring(Operation.SCALE_DOWN)); KafkaResource.replaceKafkaResource(CLUSTER_NAME, k -> k.getSpec().getKafka().setReplicas(scaledDownReplicas)); StatefulSetUtils.waitForAllStatefulSetPodsReady(KafkaResources.kafkaStatefulSetName(CLUSTER_NAME), scaledDownReplicas); PodUtils.verifyThatRunningPodsAreStable(CLUSTER_NAME); // set annotation to trigger Kafka rolling update kubeClient().statefulSet(KafkaResources.kafkaStatefulSetName(CLUSTER_NAME)).cascading(false).edit() .editMetadata() .addToAnnotations(Annotations.ANNO_STRIMZI_IO_MANUAL_ROLLING_UPDATE, "true") .endMetadata() .done(); StatefulSetUtils.waitTillSsHasRolled(KafkaResources.kafkaStatefulSetName(CLUSTER_NAME), kafkaPods); } @Test void testKafkaTopicRFLowerThanMinInSyncReplicas() { KafkaResource.kafkaPersistent(CLUSTER_NAME, 3, 3).done(); KafkaTopicResource.topic(CLUSTER_NAME, TOPIC_NAME, 1, 1).done(); String kafkaName = KafkaResources.kafkaStatefulSetName(CLUSTER_NAME); Map<String, String> kafkaPods = StatefulSetUtils.ssSnapshot(kafkaName); LOGGER.info("Setting KafkaTopic's min.insync.replicas to be higher than replication factor"); KafkaTopicResource.replaceTopicResource(TOPIC_NAME, kafkaTopic -> kafkaTopic.getSpec().getConfig().replace("min.insync.replicas", 2)); // rolling update for kafka LOGGER.info("Annotate Kafka StatefulSet {} with manual rolling update annotation", kafkaName); timeMeasuringSystem.setOperationID(timeMeasuringSystem.startTimeMeasuring(Operation.ROLLING_UPDATE)); // set annotation to trigger Kafka rolling update kubeClient().statefulSet(kafkaName).cascading(false).edit() .editMetadata() .addToAnnotations(Annotations.ANNO_STRIMZI_IO_MANUAL_ROLLING_UPDATE, "true") .endMetadata() .done(); StatefulSetUtils.waitTillSsHasRolled(kafkaName, 3, kafkaPods); assertThat(StatefulSetUtils.ssSnapshot(kafkaName), is(not(kafkaPods))); } @Override protected void recreateTestEnv(String coNamespace, List<String> bindingsNamespaces) throws Exception { super.recreateTestEnv(coNamespace, bindingsNamespaces, Constants.CO_OPERATION_TIMEOUT_DEFAULT); } @BeforeAll void setup() throws Exception { ResourceManager.setClassResources(); installClusterOperator(NAMESPACE, Constants.CO_OPERATION_TIMEOUT_MEDIUM); } }