Kaynağa Gözat

Merge branch 'master' of github.com:provectus/kafka-ui into feature/schema_registry_views

Oleg Shuralev 4 yıl önce
ebeveyn
işleme
a167482c74

+ 1 - 1
.github/workflows/maven.yml → .github/workflows/backend.yml

@@ -1,4 +1,4 @@
-name: Java CI with Maven
+name: backend
 on:
   push:
     branches: [ '*' ]

+ 31 - 0
.github/workflows/charts.yaml

@@ -0,0 +1,31 @@
+name: charts
+on:
+  create:
+    tags:
+      - "v*.*.*"
+jobs:
+  release:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+      - run: |
+          git config user.name github-actions
+          git config user.email github-actions@github.com
+      - uses: azure/setup-helm@v1
+      - name: update appVersion
+        run: |
+          export version=${GITHUB_REF##*/}
+          sed -i "s/appVersion:.*/appVersion: ${version}/" charts/kafka-ui/Chart.yaml
+      - name:
+        run: |
+          export VERSION=${GITHUB_REF##*/}
+          MSG=$(helm package --app-version ${VERSION} charts/kafka-ui)
+          git fetch origin
+          git stash
+          git checkout -b gh-pages origin/gh-pages
+          helm repo index .
+          git add -f ${MSG##*/} index.yaml
+          git commit -m "release ${VERSION}"
+          git push

+ 77 - 0
.github/workflows/release.yaml

@@ -0,0 +1,77 @@
+name: release
+on: 
+  workflow_dispatch:
+
+jobs:
+  release:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - run: |
+          git config user.name github-actions
+          git config user.email github-actions@github.com
+      - name: Cache local Maven repository
+        uses: actions/cache@v2
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+      - uses: actions/checkout@v2
+      - name: Set up JDK 1.13
+        uses: actions/setup-java@v1
+        with:
+          java-version: 1.13
+      - name: Update development version
+        run: |
+          mvn -q versions:set -DnextSnapshot
+          git add pom.xml **/pom.xml
+          git commit -m "Increased version in pom.xml"
+          git push -f
+          git reset --hard HEAD~1
+      - name: Prepare release
+        id: prep
+        run: |
+          mvn -q versions:set -DremoveSnapshot
+          export VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
+          git add .
+          git commit -m "release ${VERSION}"
+          git tag -f v${VERSION}
+          git push --tags
+          echo ::set-output name=version::${VERSION}
+      - name: Build with Maven
+        run: mvn clean package -Pprod
+#################
+#               #
+# Docker images #
+#               #
+#################
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v1
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v1
+      - name: Cache Docker layers
+        uses: actions/cache@v2
+        with:
+          path: /tmp/.buildx-cache
+          key: ${{ runner.os }}-buildx-${{ github.sha }}
+          restore-keys: |
+            ${{ runner.os }}-buildx-
+      - name: Login to DockerHub
+        if: github.ref == 'refs/heads/master'
+        uses: docker/login-action@v1 
+        with:
+          username: ${{ secrets.DOCKERHUB_USERNAME }}
+          password: ${{ secrets.DOCKERHUB_TOKEN }}
+      - name: Build and push
+        id: docker_build
+        uses: docker/build-push-action@v2
+        with:
+          builder: ${{ steps.buildx.outputs.name }}
+          context: kafka-ui-api
+          push: github.ref == 'refs/heads/master'
+          tags: provectuslabs/kafka-ui:${{ steps.prep.outputs.version }}
+          build-args: |
+            JAR_FILE=kafka-ui-api-${{ steps.prep.outputs.version }}.jar
+          cache-from: type=local,src=/tmp/.buildx-cache
+          cache-to: type=local,dest=/tmp/.buildx-cache

+ 0 - 0
chart/kafka-ui/.helmignore → charts/kafka-ui/.helmignore


+ 2 - 1
chart/kafka-ui/Chart.yaml → charts/kafka-ui/Chart.yaml

@@ -3,4 +3,5 @@ name: kafka-ui
 description: A Helm chart for kafka-UI
 type: application
 version: 0.0.1
-appVersion: 0.0.9
+appVersion: latest
+icon: https://github.com/provectus/kafka-ui/raw/master/images/kafka-ui-logo.png

+ 0 - 0
chart/kafka-ui/README.md → charts/kafka-ui/README.md


+ 0 - 0
chart/kafka-ui/templates/NOTES.txt → charts/kafka-ui/templates/NOTES.txt


+ 0 - 0
chart/kafka-ui/templates/_helpers.tpl → charts/kafka-ui/templates/_helpers.tpl


+ 0 - 0
chart/kafka-ui/templates/configmap.yaml → charts/kafka-ui/templates/configmap.yaml


+ 0 - 0
chart/kafka-ui/templates/deployment.yaml → charts/kafka-ui/templates/deployment.yaml


+ 0 - 0
chart/kafka-ui/templates/hpa.yaml → charts/kafka-ui/templates/hpa.yaml


+ 0 - 0
chart/kafka-ui/templates/ingress.yaml → charts/kafka-ui/templates/ingress.yaml


+ 0 - 0
chart/kafka-ui/templates/secret.yaml → charts/kafka-ui/templates/secret.yaml


+ 0 - 0
chart/kafka-ui/templates/service.yaml → charts/kafka-ui/templates/service.yaml


+ 0 - 0
chart/kafka-ui/templates/serviceaccount.yaml → charts/kafka-ui/templates/serviceaccount.yaml


+ 0 - 0
chart/kafka-ui/values.yaml → charts/kafka-ui/values.yaml


+ 18 - 10
kafka-ui-react-app/src/components/Dashboard/ClustersWidget/ClusterWidget.tsx

@@ -1,8 +1,8 @@
 import React from 'react';
-import formatBytes from 'lib/utils/formatBytes';
 import { NavLink } from 'react-router-dom';
-import { clusterBrokersPath } from 'lib/paths';
+import { clusterBrokersPath, clusterTopicsPath } from 'lib/paths';
 import { Cluster, ServerStatus } from 'generated-sources';
+import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted';
 
 interface ClusterWidgetProps {
   cluster: Cluster;
@@ -19,9 +19,9 @@ const ClusterWidget: React.FC<ClusterWidgetProps> = ({
     onlinePartitionCount,
   },
 }) => (
-  <NavLink to={clusterBrokersPath(name)} className="column is-full-modile is-6">
-    <div className="box is-hoverable">
-      <div className="title is-6 has-text-overflow-ellipsis" title={name}>
+  <div className="column is-full-modile is-6">
+    <div className="box">
+      <div className="title is-6 has-text-overflow-ellipsis">
         <div
           className={`tag has-margin-right ${
             status === ServerStatus.Online ? 'is-primary' : 'is-danger'
@@ -36,7 +36,9 @@ const ClusterWidget: React.FC<ClusterWidgetProps> = ({
         <tbody>
           <tr>
             <th>Brokers</th>
-            <td>{brokerCount}</td>
+            <td>
+              <NavLink to={clusterBrokersPath(name)}>{brokerCount}</NavLink>
+            </td>
           </tr>
           <tr>
             <th>Partitions</th>
@@ -44,20 +46,26 @@ const ClusterWidget: React.FC<ClusterWidgetProps> = ({
           </tr>
           <tr>
             <th>Topics</th>
-            <td>{topicCount}</td>
+            <td>
+              <NavLink to={clusterTopicsPath(name)}>{topicCount}</NavLink>
+            </td>
           </tr>
           <tr>
             <th>Production</th>
-            <td>{formatBytes(bytesInPerSec || 0)}</td>
+            <td>
+              <BytesFormatted value={bytesInPerSec} />
+            </td>
           </tr>
           <tr>
             <th>Consumption</th>
-            <td>{formatBytes(bytesOutPerSec || 0)}</td>
+            <td>
+              <BytesFormatted value={bytesOutPerSec} />
+            </td>
           </tr>
         </tbody>
       </table>
     </div>
-  </NavLink>
+  </div>
 );
 
 export default ClusterWidget;

+ 73 - 0
kafka-ui-react-app/src/components/Dashboard/ClustersWidget/__test__/ClusterWidget.spec.tsx

@@ -0,0 +1,73 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import { ServerStatus } from 'generated-sources';
+import { clusterBrokersPath, clusterTopicsPath } from 'lib/paths';
+import ClusterWidget from '../ClusterWidget';
+import { offlineCluster, onlineCluster } from './fixtures';
+
+describe('ClusterWidget', () => {
+  describe('when cluster is online', () => {
+    it('renders with correct tag', () => {
+      const tag = shallow(<ClusterWidget cluster={onlineCluster} />).find(
+        '.tag'
+      );
+      expect(tag.hasClass('is-primary')).toBeTruthy();
+      expect(tag.text()).toEqual(ServerStatus.Online);
+    });
+
+    it('renders table', () => {
+      const table = shallow(<ClusterWidget cluster={onlineCluster} />).find(
+        'table'
+      );
+      expect(table.hasClass('is-fullwidth')).toBeTruthy();
+
+      expect(
+        table.find(`NavLink[to="${clusterBrokersPath(onlineCluster.name)}"]`)
+          .exists
+      ).toBeTruthy();
+      expect(
+        table.find(`NavLink[to="${clusterTopicsPath(onlineCluster.name)}"]`)
+          .exists
+      ).toBeTruthy();
+    });
+
+    it('matches snapshot', () => {
+      expect(
+        shallow(<ClusterWidget cluster={onlineCluster} />)
+      ).toMatchSnapshot();
+    });
+  });
+
+  describe('when cluster is offline', () => {
+    it('renders with correct tag', () => {
+      const tag = shallow(<ClusterWidget cluster={offlineCluster} />).find(
+        '.tag'
+      );
+
+      expect(tag.hasClass('is-danger')).toBeTruthy();
+      expect(tag.text()).toEqual(ServerStatus.Offline);
+    });
+
+    it('renders table', () => {
+      const table = shallow(<ClusterWidget cluster={offlineCluster} />).find(
+        'table'
+      );
+      expect(table.hasClass('is-fullwidth')).toBeTruthy();
+
+      expect(
+        table.find(`NavLink[to="${clusterBrokersPath(onlineCluster.name)}"]`)
+          .exists
+      ).toBeTruthy();
+      expect(
+        table.find(`NavLink[to="${clusterTopicsPath(onlineCluster.name)}"]`)
+          .exists
+      ).toBeTruthy();
+    });
+
+    it('matches snapshot', () => {
+      expect(
+        shallow(<ClusterWidget cluster={offlineCluster} />)
+      ).toMatchSnapshot();
+    });
+  });
+});

+ 159 - 0
kafka-ui-react-app/src/components/Dashboard/ClustersWidget/__test__/__snapshots__/ClusterWidget.spec.tsx.snap

@@ -0,0 +1,159 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ClusterWidget when cluster is offline matches snapshot 1`] = `
+<div
+  className="column is-full-modile is-6"
+>
+  <div
+    className="box"
+  >
+    <div
+      className="title is-6 has-text-overflow-ellipsis"
+    >
+      <div
+        className="tag has-margin-right is-danger"
+      >
+        offline
+      </div>
+      local
+    </div>
+    <table
+      className="table is-fullwidth"
+    >
+      <tbody>
+        <tr>
+          <th>
+            Brokers
+          </th>
+          <td>
+            <NavLink
+              to="/ui/clusters/local/brokers"
+            >
+              1
+            </NavLink>
+          </td>
+        </tr>
+        <tr>
+          <th>
+            Partitions
+          </th>
+          <td>
+            2
+          </td>
+        </tr>
+        <tr>
+          <th>
+            Topics
+          </th>
+          <td>
+            <NavLink
+              to="/ui/clusters/local/topics"
+            >
+              2
+            </NavLink>
+          </td>
+        </tr>
+        <tr>
+          <th>
+            Production
+          </th>
+          <td>
+            <BytesFormatted
+              value={8000.00000673768}
+            />
+          </td>
+        </tr>
+        <tr>
+          <th>
+            Consumption
+          </th>
+          <td>
+            <BytesFormatted
+              value={0.815306356729712}
+            />
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</div>
+`;
+
+exports[`ClusterWidget when cluster is online matches snapshot 1`] = `
+<div
+  className="column is-full-modile is-6"
+>
+  <div
+    className="box"
+  >
+    <div
+      className="title is-6 has-text-overflow-ellipsis"
+    >
+      <div
+        className="tag has-margin-right is-primary"
+      >
+        online
+      </div>
+      secondLocal
+    </div>
+    <table
+      className="table is-fullwidth"
+    >
+      <tbody>
+        <tr>
+          <th>
+            Brokers
+          </th>
+          <td>
+            <NavLink
+              to="/ui/clusters/secondLocal/brokers"
+            >
+              1
+            </NavLink>
+          </td>
+        </tr>
+        <tr>
+          <th>
+            Partitions
+          </th>
+          <td>
+            6
+          </td>
+        </tr>
+        <tr>
+          <th>
+            Topics
+          </th>
+          <td>
+            <NavLink
+              to="/ui/clusters/secondLocal/topics"
+            >
+              3
+            </NavLink>
+          </td>
+        </tr>
+        <tr>
+          <th>
+            Production
+          </th>
+          <td>
+            <BytesFormatted
+              value={0.00003061819685376472}
+            />
+          </td>
+        </tr>
+        <tr>
+          <th>
+            Consumption
+          </th>
+          <td>
+            <BytesFormatted
+              value={5.737800890036267}
+            />
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</div>
+`;

+ 25 - 0
kafka-ui-react-app/src/components/Dashboard/ClustersWidget/__test__/fixtures.ts

@@ -0,0 +1,25 @@
+import { Cluster, ServerStatus } from 'generated-sources';
+
+export const onlineCluster: Cluster = {
+  name: 'secondLocal',
+  defaultCluster: false,
+  status: ServerStatus.Online,
+  brokerCount: 1,
+  onlinePartitionCount: 6,
+  topicCount: 3,
+  bytesInPerSec: 0.000030618196853764715,
+  bytesOutPerSec: 5.737800890036267075817,
+};
+
+export const offlineCluster: Cluster = {
+  name: 'local',
+  defaultCluster: true,
+  status: ServerStatus.Offline,
+  brokerCount: 1,
+  onlinePartitionCount: 2,
+  topicCount: 2,
+  bytesInPerSec: 8000.0000067376808542600021,
+  bytesOutPerSec: 0.8153063567297119490871,
+};
+
+export const clusters: Cluster[] = [onlineCluster, offlineCluster];

+ 13 - 8
kafka-ui-react-app/src/components/common/BytesFormatted/BytesFormatted.tsx

@@ -5,18 +5,23 @@ interface Props {
   precision?: number;
 }
 
-const BytesFormatted: React.FC<Props> = ({ value, precision }) => {
-  const formatBytes = React.useCallback(() => {
-    const numVal = typeof value === 'string' ? parseInt(value, 10) : value;
-    if (!numVal) return 0;
-    const pow = Math.floor(Math.log2(numVal) / 10);
+const BytesFormatted: React.FC<Props> = ({ value, precision = 0 }) => {
+  const formatedValue = React.useMemo(() => {
+    const bytes = typeof value === 'string' ? parseInt(value, 10) : value;
+
+    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+    if (!bytes || bytes === 0) return [0, sizes[0]];
+
+    if (bytes < 1024) return [Math.ceil(bytes), sizes[0]];
+
+    const pow = Math.floor(Math.log2(bytes) / 10);
     const multiplier = 10 ** (precision || 2);
     return (
-      Math.round((numVal * multiplier) / 1024 ** pow) / multiplier +
-      ['Bytes', 'KB', 'MB', 'GB', 'TB'][pow]
+      Math.round((bytes * multiplier) / 1024 ** pow) / multiplier + sizes[pow]
     );
   }, [value]);
-  return <span>{formatBytes()}</span>;
+
+  return <span>{formatedValue}</span>;
 };
 
 export default BytesFormatted;

+ 0 - 13
kafka-ui-react-app/src/lib/utils/formatBytes.ts

@@ -1,13 +0,0 @@
-function formatBytes(bytes: number, decimals = 0) {
-  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
-  if (bytes === 0) return [0, sizes[0]];
-
-  const k = 1024;
-  const dm = decimals < 0 ? 0 : decimals;
-
-  const i = Math.floor(Math.log(bytes) / Math.log(k));
-
-  return [parseFloat((bytes / k ** i).toFixed(dm)), sizes[i]];
-}
-
-export default formatBytes;

+ 0 - 20
kafka-ui-react-app/src/theme/bulma_overrides.scss

@@ -1,13 +1,3 @@
-@import "../../node_modules/bulma/sass/utilities/_all.sass";
-@import "../../node_modules/bulma/sass/base/_all.sass";
-@import "../../node_modules/bulma/sass/elements/_all.sass";
-@import "../../node_modules/bulma/sass/form/_all.sass";
-@import "../../node_modules/bulma/sass/helpers/_all.sass";
-@import "../../node_modules/bulma/sass/components/_all.sass";
-@import "../../node_modules/bulma/sass/grid/_all.sass";
-@import "../../node_modules/bulma/sass/layout/_all.sass";
-@import "../../node_modules/bulma-switch/src/sass/index.sass";
-
 .has {
   &-text-overflow-ellipsis {
     flex: 1;
@@ -55,16 +45,6 @@
   }
 }
 
-.box {
-  &.is-hoverable {
-    cursor: pointer;
-
-    &:hover {
-      box-shadow: 0 0.5em 1em -0.125em rgba(10, 10, 10, 0.2), 0 0px 0 1px rgba(10, 10, 10, 0.02);
-    }
-  }
-}
-
 @keyframes fadein {
   from { opacity: 0; }
   to   { opacity: 1; }

+ 2 - 0
kafka-ui-react-app/src/theme/index.scss

@@ -1,3 +1,5 @@
+@import 'bulma';
+@import '~bulma-switch';
 @import 'src/theme/bulma_overrides';
 
 #root, body, html {