diff --git a/README.md b/README.md
index 11f4568b27..db4df22f28 100644
--- a/README.md
+++ b/README.md
@@ -120,6 +120,8 @@ To be continued
# Configuration
+We have a plenty of docker-compose files as examples. Please check them out in ``docker`` directory.
+
## Configuration File
Example of how to configure clusters in the [application-local.yml](https://github.com/provectus/kafka-ui/blob/master/kafka-ui-api/src/main/resources/application-local.yml) configuration file:
diff --git a/docker/kafka-ui-zookeeper-ssl.yml b/docker/kafka-ui-zookeeper-ssl.yml
new file mode 100644
index 0000000000..0ae5381b64
--- /dev/null
+++ b/docker/kafka-ui-zookeeper-ssl.yml
@@ -0,0 +1,145 @@
+---
+version: '2'
+services:
+
+ kafka-ui:
+ container_name: kafka-ui
+ image: provectuslabs/kafka-ui:latest
+ ports:
+ - 8080:8080
+ - 5005:5005
+ volumes:
+ - /tmp/kafka/secrets/kafka.kafka1.keystore.jks:/etc/kafka/secrets/kafka.zookeeper.keystore.jks
+ - /tmp/kafka/secrets/kafka.zookeeper.truststore.jks:/etc/kafka/secrets/kafka.zookeeper.truststore.jks
+ depends_on:
+ - zookeeper0
+ - kafka0
+ - schemaregistry0
+ - kafka-connect0
+ environment:
+ KAFKA_CLUSTERS_0_NAME: local
+ KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka0:29092
+ KAFKA_CLUSTERS_0_ZOOKEEPER: zookeeper0:2182
+ KAFKA_CLUSTERS_0_JMXPORT: 9997
+ KAFKA_CLUSTERS_0_SCHEMAREGISTRY: http://schemaregistry0:8085
+ KAFKA_CLUSTERS_0_KAFKACONNECT_0_NAME: first
+ KAFKA_CLUSTERS_0_KAFKACONNECT_0_ADDRESS: http://kafka-connect0:8083
+ KAFKA_CLUSTERS_0_ZOOKEEPER_CLIENTCNXNSOCKET: org.apache.zookeeper.ClientCnxnSocketNetty
+ KAFKA_CLUSTERS_0_ZOOKEEPER_CLIENT_SECURE: 'true'
+ KAFKA_CLUSTERS_0_ZOOKEEPER_SSL_KEYSTORE_LOCATION: /etc/kafka/secrets/kafka.zookeeper.keystore.jks
+ KAFKA_CLUSTERS_0_ZOOKEEPER_SSL_KEYSTORE_PASSWORD: 12345678
+ KAFKA_CLUSTERS_0_ZOOKEEPER_SSL_TRUSTSTORE_LOCATION: /etc/kafka/secrets/kafka.zookeeper.truststore.jks
+ KAFKA_CLUSTERS_0_ZOOKEEPER_SSL_TRUSTSTORE_PASSWORD: 12345678
+
+ zookeeper0:
+ image: confluentinc/cp-zookeeper:5.2.4
+ volumes:
+ - /tmp/kafka/secrets/kafka.kafka1.keystore.jks:/etc/kafka/secrets/kafka.zookeeper.keystore.jks
+ - /tmp/kafka/secrets/kafka.zookeeper.truststore.jks:/etc/kafka/secrets/kafka.zookeeper.truststore.jks
+ environment:
+ ZOOKEEPER_CLIENT_PORT: 2182
+ ZOOKEEPER_TICK_TIME: 2000
+
+ ZOOKEEPER_SECURE_CLIENT_PORT: 2182
+ ZOOKEEPER_SERVER_CNXN_FACTORY: org.apache.zookeeper.server.NettyServerCnxnFactory
+ ZOOKEEPER_SSL_KEYSTORE_LOCATION: /etc/kafka/secrets/kafka.zookeeper.keystore.jks
+ ZOOKEEPER_SSL_KEYSTORE_PASSWORD: 12345678
+ ZOOKEEPER_SSL_KEYSTORE_TYPE: PKCS12
+ ZOOKEEPER_SSL_TRUSTSTORE_LOCATION: /etc/kafka/secrets/kafka.zookeeper.truststore.jks
+ ZOOKEEPER_SSL_TRUSTSTORE_PASSWORD: 12345678
+ ZOOKEEPER_SSL_TRUSTSTORE_TYPE: JKS
+ # TLS 1.2 is the tested-default - TLS 1.3 has not been tested for production
+ # You can evaluate TLS 1.3 for ZooKeeper by uncommenting the following two properties
+ # and setting KAFKA_ZOOKEEPER_SSL_PROTOCOL on brokers
+ ZOOKEEPER_SSL_ENABLED_PROTOCOLS: TLSv1.3,TLSv1.2
+ ZOOKEEPER_SSL_QUORUM_ENABLED_PROTOCOLS: TLSv1.3,TLSv1.2
+ ZOOKEEPER_SSL_CIPHER_SUITES: TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ ZOOKEEPER_SSL_CLIENT_AUTH: need
+ ZOOKEEPER_AUTH_PROVIDER_X509: org.apache.zookeeper.server.auth.X509AuthenticationProvider
+ ZOOKEEPER_AUTH_PROVIDER_SASL: org.apache.zookeeper.server.auth.SASLAuthenticationProvider
+ ports:
+ - 2182:2182
+
+ kafka0:
+ image: confluentinc/cp-kafka:5.2.4
+ depends_on:
+ - zookeeper0
+ ports:
+ - 9092:9092
+ - 9997:9997
+ volumes:
+ - /tmp/kafka/secrets/kafka.kafka1.keystore.jks:/etc/kafka/secrets/kafka.kafka1.keystore.jks
+ - /tmp/kafka/secrets/kafka.server.truststore.jks:/etc/kafka/secrets/kafka.kafka1.truststore.jks
+ environment:
+ KAFKA_BROKER_ID: 1
+ KAFKA_ZOOKEEPER_CONNECT: zookeeper0:2182
+ KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka0:29092,PLAINTEXT_HOST://localhost:9092
+ KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
+ KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
+ KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
+ JMX_PORT: 9997
+ KAFKA_JMX_OPTS: -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=kafka0 -Dcom.sun.management.jmxremote.rmi.port=9997
+ KAFKA_ZOOKEEPER_SSL_CLIENT_ENABLE: 'true'
+ KAFKA_ZOOKEEPER_SSL_CIPHER_SUITES: TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ KAFKA_ZOOKEEPER_CLIENT_CNXN_SOCKET: org.apache.zookeeper.ClientCnxnSocketNetty
+ KAFKA_ZOOKEEPER_SSL_KEYSTORE_LOCATION: /etc/kafka/secrets/kafka.kafka1.keystore.jks
+ KAFKA_ZOOKEEPER_SSL_KEYSTORE_PASSWORD: 12345678
+ KAFKA_ZOOKEEPER_SSL_KEYSTORE_TYPE: PKCS12
+ KAFKA_ZOOKEEPER_SSL_TRUSTSTORE_LOCATION: /etc/kafka/secrets/kafka.kafka1.truststore.jks
+ KAFKA_ZOOKEEPER_SSL_TRUSTSTORE_PASSWORD: 12345678
+ KAFKA_ZOOKEEPER_SSL_TRUSTSTORE_TYPE: JKS
+
+ schemaregistry0:
+ image: confluentinc/cp-schema-registry:5.2.4
+ ports:
+ - 8085:8085
+ depends_on:
+ - zookeeper0
+ - kafka0
+ environment:
+ SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: PLAINTEXT://kafka0:29092
+ SCHEMA_REGISTRY_KAFKASTORE_CONNECTION_URL: zookeeper0:2182
+ SCHEMA_REGISTRY_KAFKASTORE_SECURITY_PROTOCOL: PLAINTEXT
+ SCHEMA_REGISTRY_HOST_NAME: schemaregistry0
+ SCHEMA_REGISTRY_LISTENERS: http://schemaregistry0:8085
+
+ SCHEMA_REGISTRY_SCHEMA_REGISTRY_INTER_INSTANCE_PROTOCOL: "http"
+ SCHEMA_REGISTRY_LOG4J_ROOT_LOGLEVEL: INFO
+ SCHEMA_REGISTRY_KAFKASTORE_TOPIC: _schemas
+
+ kafka-connect0:
+ image: confluentinc/cp-kafka-connect:5.2.4
+ ports:
+ - 8083:8083
+ depends_on:
+ - kafka0
+ - schemaregistry0
+ environment:
+ CONNECT_BOOTSTRAP_SERVERS: kafka0:29092
+ CONNECT_GROUP_ID: compose-connect-group
+ CONNECT_CONFIG_STORAGE_TOPIC: _connect_configs
+ CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: 1
+ CONNECT_OFFSET_STORAGE_TOPIC: _connect_offset
+ CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: 1
+ CONNECT_STATUS_STORAGE_TOPIC: _connect_status
+ CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: 1
+ CONNECT_KEY_CONVERTER: org.apache.kafka.connect.storage.StringConverter
+ CONNECT_KEY_CONVERTER_SCHEMA_REGISTRY_URL: http://schemaregistry0:8085
+ CONNECT_VALUE_CONVERTER: org.apache.kafka.connect.storage.StringConverter
+ CONNECT_VALUE_CONVERTER_SCHEMA_REGISTRY_URL: http://schemaregistry0:8085
+ CONNECT_INTERNAL_KEY_CONVERTER: org.apache.kafka.connect.json.JsonConverter
+ CONNECT_INTERNAL_VALUE_CONVERTER: org.apache.kafka.connect.json.JsonConverter
+ CONNECT_REST_ADVERTISED_HOST_NAME: kafka-connect0
+ CONNECT_PLUGIN_PATH: "/usr/share/java,/usr/share/confluent-hub-components"
+
+ kafka-init-topics:
+ image: confluentinc/cp-kafka:5.2.4
+ volumes:
+ - ./message.json:/data/message.json
+ depends_on:
+ - kafka0
+ command: "bash -c 'echo Waiting for Kafka to be ready... && \
+ cub kafka-ready -b kafka0:29092 1 30 && \
+ kafka-topics --create --topic second.users --partitions 3 --replication-factor 1 --if-not-exists --zookeeper zookeeper0:2182 && \
+ kafka-topics --create --topic first.messages --partitions 2 --replication-factor 1 --if-not-exists --zookeeper zookeeper0:2182 && \
+ kafka-console-producer --broker-list kafka0:29092 -topic second.users < /data/message.json'"
diff --git a/kafka-ui-api/pom.xml b/kafka-ui-api/pom.xml
index ea7a888173..11c07f77d5 100644
--- a/kafka-ui-api/pom.xml
+++ b/kafka-ui-api/pom.xml
@@ -62,9 +62,9 @@
${kafka.version}
- com.101tec
- zkclient
- ${zkclient.version}
+ org.apache.zookeeper
+ zookeeper
+ ${zookeper.version}
org.projectlombok
diff --git a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/exception/ZooKeeperException.java b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/exception/ZooKeeperException.java
new file mode 100644
index 0000000000..761e8f1c1c
--- /dev/null
+++ b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/exception/ZooKeeperException.java
@@ -0,0 +1,9 @@
+package com.provectus.kafka.ui.exception;
+
+public class ZooKeeperException extends RuntimeException {
+
+ public ZooKeeperException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/KafkaService.java b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/KafkaService.java
index 13ec1e87c6..1a9852eaa7 100644
--- a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/KafkaService.java
+++ b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/KafkaService.java
@@ -159,8 +159,9 @@ public class KafkaService {
ServerStatus zookeeperStatus = ServerStatus.OFFLINE;
Throwable zookeeperException = null;
try {
- zookeeperStatus = zookeeperService.isZookeeperOnline(currentCluster) ? ServerStatus.ONLINE :
- ServerStatus.OFFLINE;
+ zookeeperStatus = zookeeperService.isZookeeperOnline(currentCluster)
+ ? ServerStatus.ONLINE
+ : ServerStatus.OFFLINE;
} catch (Throwable e) {
zookeeperException = e;
}
diff --git a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/ZookeeperService.java b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/ZookeeperService.java
index 02123ac957..31bd317e78 100644
--- a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/ZookeeperService.java
+++ b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/ZookeeperService.java
@@ -1,11 +1,15 @@
package com.provectus.kafka.ui.service;
+import com.provectus.kafka.ui.exception.ZooKeeperException;
import com.provectus.kafka.ui.model.KafkaCluster;
+import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
-import org.I0Itec.zkclient.ZkClient;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.ZooKeeper;
+import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@@ -14,7 +18,7 @@ import org.springframework.util.StringUtils;
@Log4j2
public class ZookeeperService {
- private final Map cachedZkClient = new ConcurrentHashMap<>();
+ private final Map cachedZkClient = new ConcurrentHashMap<>();
public boolean isZookeeperOnline(KafkaCluster kafkaCluster) {
var isConnected = false;
@@ -28,20 +32,41 @@ public class ZookeeperService {
return isConnected;
}
- private boolean isZkClientConnected(ZkClient zkClient) {
- zkClient.getChildren("/brokers/ids");
+ private boolean isZkClientConnected(ZooKeeper zkClient) {
+ try {
+ zkClient.getChildren("/brokers/ids", null);
+ } catch (KeeperException e) {
+ log.error("A zookeeper exception has occurred", e);
+ return false;
+ } catch (InterruptedException e) {
+ log.error("Interrupted: ", e);
+ Thread.currentThread().interrupt();
+ }
return true;
}
- private ZkClient getOrCreateZkClient(KafkaCluster cluster) {
+ @Nullable
+ private ZooKeeper getOrCreateZkClient(KafkaCluster cluster) {
+ final var clusterName = cluster.getName();
+ final var client = cachedZkClient.get(clusterName);
+ if (client != null && client.getState() != ZooKeeper.States.CONNECTED) {
+ cachedZkClient.remove(clusterName);
+ }
try {
- return cachedZkClient.computeIfAbsent(
- cluster.getName(),
- (n) -> new ZkClient(cluster.getZookeeper(), 1000)
- );
+ return cachedZkClient.computeIfAbsent(clusterName, n -> createClient(cluster));
} catch (Exception e) {
- log.error("Error while creating zookeeper client for cluster {}", cluster.getName());
+ log.error("Error while creating zookeeper client for cluster {}", clusterName);
return null;
}
}
+
+ private ZooKeeper createClient(KafkaCluster cluster) {
+ try {
+ return new ZooKeeper(cluster.getZookeeper(), 60 * 1000, watchedEvent -> {});
+ } catch (IOException e) {
+ log.error("Error while creating a zookeeper client for cluster [{}]",
+ cluster.getName());
+ throw new ZooKeeperException(e);
+ }
+ }
}
diff --git a/pom.xml b/pom.xml
index 0c74e664aa..d1ba567fa8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,68 +1,90 @@
- 4.0.0
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
pom
kafka-ui-contract
- kafka-ui-api
- kafka-ui-e2e-checks
-
+ kafka-ui-api
+ kafka-ui-e2e-checks
+
-
- 13
- 13
- UTF-8
+
+ 13
+ 13
+ UTF-8
- 2.2.4.RELEASE
- 0.2.1
- 1.3.1.Final
- 1.18.10
+ 2.2.4.RELEASE
+ 0.2.1
+ 1.3.1.Final
+ 1.18.10
1.18.20
- latest
- 0.11
- 2.4.1
- v14.17.1
- 1.4.10
- 1.8.0
- 3.5.1
- 3.1.0
- 3.1.0
- 2.22.0
- 4.3.0
- 1.6.0
- 1.2.32
- 2.4.1
- 1.9.2
- 5.5.1
- 2.2
- 1.15.1
- 5.4.0
- 2.21.0
- 3.19.0
+ latest
+ 3.5.7
+ 2.4.1
+ v14.17.1
+ 1.4.10
+ 1.8.0
+ 3.5.1
+ 3.1.0
+ 3.1.0
+ 2.22.0
+ 4.3.0
+ 1.6.0
+ 1.2.32
+ 2.4.1
+ 1.9.2
+ 5.5.1
+ 2.2
+ 1.15.1
+ 5.4.0
+ 2.21.0
+ 3.19.0
- ..//kafka-ui-react-app/src/generated-sources
- provectus
- https://sonarcloud.io
-
+ ..//kafka-ui-react-app/src/generated-sources
+
+ provectus
+ https://sonarcloud.io
+
-
-
- confluent
- https://packages.confluent.io/maven/
-
-
+
+
+ confluent
+ https://packages.confluent.io/maven/
+
+
+ central
+ Central Repository
+ https://repo.maven.apache.org/maven2
+ default
+
+ false
+
+
+
-
-
- confluent
- https://packages.confluent.io/maven/
-
-
+
+
+ confluent
+ https://packages.confluent.io/maven/
+
+
+ central
+ Central Repository
+ https://repo.maven.apache.org/maven2
+ default
+
+ false
+
+
+ never
+
+
+
- com.provectus
- kafka-ui
- 0.1.1-SNAPSHOT
- kafka-ui
- Kafka metrics for UI panel
+ com.provectus
+ kafka-ui
+ 0.1.1-SNAPSHOT
+ kafka-ui
+ Kafka metrics for UI panel