Parcourir la source

Merge branch 'master' into vlad/develop

VladSenyuta il y a 2 ans
Parent
commit
e951d0d49c

+ 19 - 11
.github/ISSUE_TEMPLATE/bug_report.md

@@ -15,33 +15,36 @@ https://github.com/provectus/kafka-ui/discussions
 
 -->
 
-**Describe the bug**
-<!--(A clear and concise description of what the bug is.)-->
+<!--
+Please follow the naming conventions for bugs:
+<Feature/Area/Scope> :  <Compact, but specific problem summary> 
+Avoid generic titles, like “Topics: incorrect layout of message sorting drop-down list”. Better use something like: “Topics: Message sorting drop-down list overlaps the "Submit" button”.
+
+-->
 
+**Describe the bug** (Actual behavior)
+<!--(A clear and concise description of what the bug is.Use a list, if there is more than one problem)-->
+
+**Expected behavior**
+<!--(A clear and concise description of what you expected to happen.)-->
 
 **Set up**
 <!--
+WE MIGHT CLOSE THE ISSUE without further explanation IF YOU DON'T PROVIDE THIS INFORMATION.
+
 How do you run the app? Please provide as much info as possible:
 1. App version (docker image version or check commit hash in the top left corner in UI)
 2. Helm chart version, if you use one
 3. Any IAAC configs
-
-We might close the issue without further explanation if you don't provide such information.
 -->
 
 
 **Steps to Reproduce**
 <!-- We'd like you to provide an example setup (via docker-compose, helm, etc.) 
 to reproduce the problem, especially with a complex setups. -->
-Steps to reproduce the behavior:
 
 1. 
 
-**Expected behavior**
-<!--
-(A clear and concise description of what you expected to happen)
--->
-
 **Screenshots**
 <!--
 (If applicable, add screenshots to help explain your problem)
@@ -50,5 +53,10 @@ Steps to reproduce the behavior:
 
 **Additional context**
 <!--
-(Add any other context about the problem here)
+Add any other context about the problem here. E.g.: 
+1. Are there any alternative scenarios (different data/methods/configuration/setup) you have tried? 
+   Were they successfull or same issue occured? Please provide steps as well.
+2. Related issues (if there are any).
+3. Logs (if available)
+4. Is there any serious impact or behaviour on the end-user because of this issue, that can be overlooked?
 -->

+ 54 - 12
documentation/compose/e2e-tests.yaml

@@ -7,10 +7,18 @@ services:
     image: provectuslabs/kafka-ui:latest
     ports:
       - 8080:8080
+    healthcheck:
+      test: wget --no-verbose --tries=1 --spider  http://localhost:8080/actuator/health
+      interval: 30s
+      timeout: 10s
+      retries: 10  
     depends_on:
-      - kafka0
-      - schemaregistry0
-      - kafka-connect0
+        kafka0:
+          condition: service_healthy
+        schemaregistry0:
+          condition: service_healthy
+        kafka-connect0:
+          condition: service_healthy
     environment:
       KAFKA_CLUSTERS_0_NAME: local
       KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka0:29092
@@ -24,6 +32,11 @@ services:
     image: confluentinc/cp-kafka:7.2.1
     hostname: kafka0
     container_name: kafka0
+    healthcheck:
+     test: unset JMX_PORT && 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=9999" && kafka-broker-api-versions --bootstrap-server=localhost:9092
+     interval: 30s
+     timeout: 10s
+     retries: 10
     ports:
       - "9092:9092"
       - "9997:9997"
@@ -54,7 +67,13 @@ services:
     ports:
       - 8085:8085
     depends_on:
-      - kafka0
+      kafka0:
+          condition: service_healthy
+    healthcheck:
+     test: ["CMD", "timeout", "1", "curl", "--silent", "--fail", "http://schemaregistry0:8085/subjects"]
+     interval: 30s
+     timeout: 10s
+     retries: 10
     environment:
       SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: PLAINTEXT://kafka0:29092
       SCHEMA_REGISTRY_KAFKASTORE_SECURITY_PROTOCOL: PLAINTEXT
@@ -73,8 +92,15 @@ services:
     ports:
       - 8083:8083
     depends_on:
-      - kafka0
-      - schemaregistry0
+      kafka0:
+          condition: service_healthy
+      schemaregistry0:
+          condition: service_healthy
+    healthcheck:
+      test: ["CMD", "nc", "127.0.0.1", "8083"]
+      interval: 30s
+      timeout: 10s
+      retries: 10
     environment:
       CONNECT_BOOTSTRAP_SERVERS: kafka0:29092
       CONNECT_GROUP_ID: compose-connect-group
@@ -100,7 +126,8 @@ services:
     volumes:
       - ./message.json:/data/message.json
     depends_on:
-      - kafka0
+      kafka0:
+          condition: service_healthy
     command: "bash -c 'echo Waiting for Kafka to be ready... && \
                cub kafka-ready -b kafka0:29092 1 30 && \
                kafka-topics --create --topic users --partitions 3 --replication-factor 1 --if-not-exists --bootstrap-server kafka0:29092 && \
@@ -114,6 +141,11 @@ services:
         image: postgres:9.6.22
     ports:
       - 5432:5432
+    healthcheck:
+      test: ["CMD-SHELL", "pg_isready -U dev_user"]
+      interval: 10s
+      timeout: 5s
+      retries: 5  
     environment:
       POSTGRES_USER: 'dev_user'
       POSTGRES_PASSWORD: '12345'
@@ -121,18 +153,28 @@ services:
   create-connectors:
     image: ellerbrock/alpine-bash-curl-ssl
     depends_on:
-      - postgres-db
-      - kafka-connect0
+      postgres-db:
+          condition: service_healthy
+      kafka-connect0:
+        condition: service_healthy
     volumes:
       - ./connectors:/connectors
     command: bash -c '/connectors/start.sh'
 
   ksqldb:
     image: confluentinc/ksqldb-server:0.18.0
+    healthcheck:
+      test: ["CMD", "timeout", "1", "curl", "--silent", "--fail", "http://localhost:8088/info"]
+      interval: 30s
+      timeout: 10s
+      retries: 10
     depends_on:
-      - kafka0
-      - kafka-connect0
-      - schemaregistry0
+      kafka0:
+        condition: service_healthy
+      kafka-connect0:
+        condition: service_healthy
+      schemaregistry0:
+         condition: service_healthy
     ports:
       - 8088:8088
     environment:

+ 0 - 2
documentation/compose/kafka-ui.yaml

@@ -41,7 +41,6 @@ services:
       KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
       KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
       KAFKA_JMX_PORT: 9997
-      KAFKA_JMX_HOSTNAME: localhost
       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_PROCESS_ROLES: 'broker,controller'
       KAFKA_NODE_ID: 1
@@ -70,7 +69,6 @@ services:
       KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
       KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
       KAFKA_JMX_PORT: 9998
-      KAFKA_JMX_HOSTNAME: localhost
       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=9998
       KAFKA_PROCESS_ROLES: 'broker,controller'
       KAFKA_NODE_ID: 1

+ 3 - 2
kafka-ui-api/src/main/java/com/provectus/kafka/ui/config/auth/OAuthProperties.java

@@ -1,6 +1,7 @@
 package com.provectus.kafka.ui.config.auth;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import javax.annotation.PostConstruct;
@@ -31,13 +32,13 @@ public class OAuthProperties {
     private String clientName;
     private String redirectUri;
     private String authorizationGrantType;
-    private Set<String> scope;
+    private Set<String> scope = new HashSet<>();
     private String issuerUri;
     private String authorizationUri;
     private String tokenUri;
     private String userInfoUri;
     private String jwkSetUri;
     private String userNameAttribute;
-    private Map<String, String> customParams;
+    private Map<String, String> customParams = new HashMap<>();
   }
 }

+ 1 - 1
kafka-ui-api/src/main/java/com/provectus/kafka/ui/config/auth/OAuthPropertiesConverter.java

@@ -71,7 +71,7 @@ public final class OAuthPropertiesConverter {
   }
 
   private static boolean isGoogle(OAuth2Provider provider) {
-    return provider.getCustomParams().get(TYPE).equalsIgnoreCase(GOOGLE);
+    return GOOGLE.equalsIgnoreCase(provider.getCustomParams().get(TYPE));
   }
 }
 

+ 2 - 4
kafka-ui-api/src/main/java/com/provectus/kafka/ui/controller/StaticController.java

@@ -53,9 +53,7 @@ public class StaticController {
   @SneakyThrows
   private String buildFile(Resource file, String contextPath) {
     return ResourceUtil.readAsString(file)
-        .replace("\"/assets/", "\"" + contextPath + "/assets/")
-        .replace("\"/favicon/", "\"" + contextPath + "/favicon/")
-        .replace("/manifest.json", contextPath + "/manifest.json")
-        .replace("window.basePath = ''", "window.basePath=\"" + contextPath + "\"");
+        .replace("\"assets/", "\"" + contextPath + "/assets/")
+        .replace("PUBLIC-PATH-VARIABLE",  contextPath);
   }
 }

+ 2 - 1
kafka-ui-api/src/test/java/com/provectus/kafka/ui/KafkaConnectServiceTests.java

@@ -1,6 +1,7 @@
 package com.provectus.kafka.ui;
 
 import static java.util.function.Predicate.not;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import com.provectus.kafka.ui.model.ConnectorDTO;
@@ -335,7 +336,7 @@ public class KafkaConnectServiceTests extends AbstractIntegrationTest {
         .exchange()
         .expectStatus().isOk()
         .expectBodyList(ConnectorPluginDTO.class)
-        .value(plugins -> assertEquals(14, plugins.size()));
+        .value(plugins -> assertThat(plugins.size()).isGreaterThan(0));
   }
 
   @Test

+ 4 - 1
kafka-ui-api/src/test/java/com/provectus/kafka/ui/container/KafkaConnectContainer.java

@@ -39,7 +39,10 @@ public class KafkaConnectContainer extends GenericContainer<KafkaConnectContaine
     withEnv("CONNECT_INTERNAL_VALUE_CONVERTER", "org.apache.kafka.connect.json.JsonConverter");
     withEnv("CONNECT_REST_ADVERTISED_HOST_NAME", "kafka-connect");
     withEnv("CONNECT_REST_PORT", String.valueOf(CONNECT_PORT));
-    withEnv("CONNECT_PLUGIN_PATH", "/usr/share/java,/usr/share/confluent-hub-components");
+    withEnv("CONNECT_PLUGIN_PATH",
+        "/usr/share/java,/usr/share/confluent-hub-components,"
+            // adding additional paths to find FileStreamSinkConnector
+            + "/usr/local/share/kafka/plugins,/usr/share/filestream-connectors");
     return self();
   }
 

+ 9 - 6
kafka-ui-react-app/index.html

@@ -3,7 +3,6 @@
   <head>
     <meta charset="utf-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1" />
-
     <!-- Google fonts -->
     <link rel="preconnect" href="https://fonts.googleapis.com" />
     <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
@@ -13,14 +12,18 @@
     />
 
     <!-- Favicons -->
-    <link rel="icon" href="/favicon/favicon.ico" sizes="any" />
-    <link rel="icon" href="/favicon/icon.svg" type="image/svg+xml" />
-    <link rel="apple-touch-icon" href="/favicon/apple-touch-icon.png" />
-    <link rel="manifest" href="/manifest.json" />
+    <link rel="icon" href="PUBLIC-PATH-VARIABLE/favicon/favicon.ico" sizes="any" />
+    <link rel="icon" href="PUBLIC-PATH-VARIABLE/favicon/icon.svg" type="image/svg+xml" />
+    <link rel="apple-touch-icon" href="PUBLIC-PATH-VARIABLE/favicon/apple-touch-icon.png" />
+    <link rel="manifest" href="PUBLIC-PATH-VARIABLE/manifest.json" />
 
     <title>UI for Apache Kafka</title>
     <script type="text/javascript">
-      window.basePath = '';
+      window.basePath = 'PUBLIC-PATH-VARIABLE';
+
+      window.__assetsPathBuilder = function (importer) {
+        return window.basePath+ "/" + importer;
+      };
     </script>
   </head>
 

+ 45 - 51
kafka-ui-react-app/src/components/Cluster/Cluster.tsx

@@ -16,23 +16,15 @@ import {
 import ClusterContext from 'components/contexts/ClusterContext';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import { useClusters } from 'lib/hooks/api/clusters';
-import Brokers from 'components/Brokers/Brokers';
-import Topics from 'components/Topics/Topics';
-import Schemas from 'components/Schemas/Schemas';
-import Connect from 'components/Connect/Connect';
-import KsqlDb from 'components/KsqlDb/KsqlDb';
-import ConsumerGroups from 'components/ConsumerGroups/ConsumerGroups';
 
-// We can't use Lazy loading till we have a better way to update publicPath in runtime
-// Now java app replaces paths in builded index.html file.
-// const Brokers = React.lazy(() => import('components/Brokers/Brokers'));
-// const Topics = React.lazy(() => import('components/Topics/Topics'));
-// const Schemas = React.lazy(() => import('components/Schemas/Schemas'));
-// const Connect = React.lazy(() => import('components/Connect/Connect'));
-// const KsqlDb = React.lazy(() => import('components/KsqlDb/KsqlDb'));
-// const ConsumerGroups = React.lazy(
-//   () => import('components/ConsumerGroups/ConsumerGroups')
-// );
+const Brokers = React.lazy(() => import('components/Brokers/Brokers'));
+const Topics = React.lazy(() => import('components/Topics/Topics'));
+const Schemas = React.lazy(() => import('components/Schemas/Schemas'));
+const Connect = React.lazy(() => import('components/Connect/Connect'));
+const KsqlDb = React.lazy(() => import('components/KsqlDb/KsqlDb'));
+const ConsumerGroups = React.lazy(
+  () => import('components/ConsumerGroups/ConsumerGroups')
+);
 
 const Cluster: React.FC = () => {
   const { clusterName } = useAppParams<ClusterNameRoute>();
@@ -59,49 +51,51 @@ const Cluster: React.FC = () => {
   return (
     <Suspense fallback={<PageLoader />}>
       <ClusterContext.Provider value={contextValue}>
-        <Routes>
-          <Route
-            path={getNonExactPath(clusterBrokerRelativePath)}
-            element={<Brokers />}
-          />
-          <Route
-            path={getNonExactPath(clusterTopicsRelativePath)}
-            element={<Topics />}
-          />
-          <Route
-            path={getNonExactPath(clusterConsumerGroupsRelativePath)}
-            element={<ConsumerGroups />}
-          />
-          {contextValue.hasSchemaRegistryConfigured && (
+        <Suspense fallback={<PageLoader />}>
+          <Routes>
             <Route
-              path={getNonExactPath(clusterSchemasRelativePath)}
-              element={<Schemas />}
+              path={getNonExactPath(clusterBrokerRelativePath)}
+              element={<Brokers />}
             />
-          )}
-          {contextValue.hasKafkaConnectConfigured && (
             <Route
-              path={getNonExactPath(clusterConnectsRelativePath)}
-              element={<Connect />}
+              path={getNonExactPath(clusterTopicsRelativePath)}
+              element={<Topics />}
             />
-          )}
-          {contextValue.hasKafkaConnectConfigured && (
             <Route
-              path={getNonExactPath(clusterConnectorsRelativePath)}
-              element={<Connect />}
+              path={getNonExactPath(clusterConsumerGroupsRelativePath)}
+              element={<ConsumerGroups />}
             />
-          )}
-          {contextValue.hasKsqlDbConfigured && (
+            {contextValue.hasSchemaRegistryConfigured && (
+              <Route
+                path={getNonExactPath(clusterSchemasRelativePath)}
+                element={<Schemas />}
+              />
+            )}
+            {contextValue.hasKafkaConnectConfigured && (
+              <Route
+                path={getNonExactPath(clusterConnectsRelativePath)}
+                element={<Connect />}
+              />
+            )}
+            {contextValue.hasKafkaConnectConfigured && (
+              <Route
+                path={getNonExactPath(clusterConnectorsRelativePath)}
+                element={<Connect />}
+              />
+            )}
+            {contextValue.hasKsqlDbConfigured && (
+              <Route
+                path={getNonExactPath(clusterKsqlDbRelativePath)}
+                element={<KsqlDb />}
+              />
+            )}
             <Route
-              path={getNonExactPath(clusterKsqlDbRelativePath)}
-              element={<KsqlDb />}
+              path="/"
+              element={<Navigate to={clusterBrokerRelativePath} replace />}
             />
-          )}
-          <Route
-            path="/"
-            element={<Navigate to={clusterBrokerRelativePath} replace />}
-          />
-        </Routes>
-        <Outlet />
+          </Routes>
+          <Outlet />
+        </Suspense>
       </ClusterContext.Provider>
     </Suspense>
   );

+ 4 - 1
kafka-ui-react-app/src/components/Cluster/__tests__/Cluster.spec.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import { Cluster, ClusterFeaturesEnum } from 'generated-sources';
 import ClusterComponent from 'components/Cluster/Cluster';
-import { screen } from '@testing-library/react';
+import { screen, waitFor } from '@testing-library/react';
 import { render, WithRoute } from 'lib/testHelpers';
 import {
   clusterBrokersPath,
@@ -59,6 +59,9 @@ describe('Cluster', () => {
       </WithRoute>,
       { initialEntries: [pathname] }
     );
+    await waitFor(() => {
+      expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
+    });
   };
 
   it('renders Brokers', async () => {

+ 20 - 0
kafka-ui-react-app/vite.config.ts

@@ -18,6 +18,26 @@ export default defineConfig(({ mode }) => {
     build: {
       outDir: 'build',
     },
+    experimental: {
+      renderBuiltUrl(
+        filename: string,
+        {
+          hostType,
+        }: {
+          hostId: string;
+          hostType: 'js' | 'css' | 'html';
+          type: 'asset' | 'public';
+        }
+      ) {
+        if (hostType === 'js') {
+          return {
+            runtime: `window.__assetsPathBuilder(${JSON.stringify(filename)})`,
+          };
+        }
+
+        return filename;
+      },
+    },
     define: {
       'process.env.NODE_ENV': `"${mode}"`,
       'process.env.VITE_TAG': `"${process.env.VITE_TAG}"`,