From 75d699af82867b41d22b68363e6458b0c2bfb876 Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Thu, 8 Feb 2024 15:43:57 +0530 Subject: [PATCH 1/7] :art: Improved design for logs component improved design for logs and disabled network_io and disk_io for apps --- .../components/charts/charts-client.tsx | 8 +++- src/apps/console/components/logger.tsx | 38 +++++++++++++++++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/apps/console/components/charts/charts-client.tsx b/src/apps/console/components/charts/charts-client.tsx index 3c8666603..1d4844001 100644 --- a/src/apps/console/components/charts/charts-client.tsx +++ b/src/apps/console/components/charts/charts-client.tsx @@ -18,7 +18,7 @@ const Chart = ( className?: string; } ) => { - const { height, width, className, title } = props; + const { height, width, className, title, disabled } = props; return (
+ {disabled && ( +
+ Not Available +
+ )} +
diff --git a/src/apps/console/components/logger.tsx b/src/apps/console/components/logger.tsx index e695c99ba..a7b7c1aa9 100644 --- a/src/apps/console/components/logger.tsx +++ b/src/apps/console/components/logger.tsx @@ -845,11 +845,41 @@ const LogComp = ({ }} > {subscribed && logs.length === 0 && ( -
-
- Connected to logs stream, and waiting for the logs to be generated. + +
+
+
+
Logs
+ +
+
+
+ +
+
+ + + +
+ + 00 matches + +
+
+
+
+
+ No logs produced in the last 3 hours. +
+
+
-
+ )} {!subscribed && logs.length === 0 && } From e3e196f07953b39ebdfc11db226a7205f70fe5c8 Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Thu, 8 Feb 2024 15:47:18 +0530 Subject: [PATCH 2/7] :bookmark: Updated build --- .github/workflows/build-container.yaml | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-container.yaml b/.github/workflows/build-container.yaml index bd840bc37..970329047 100644 --- a/.github/workflows/build-container.yaml +++ b/.github/workflows/build-container.yaml @@ -92,12 +92,19 @@ jobs: docker build --build-arg APP=${{matrix.app}} -f ${{matrix.dockerFile}} . -t "$image_name:$IMAGE_TAG" --push + - name: Build & Push Image + if: !(startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/')) + run: | + set +e + image_name="ghcr.io/${{ github.repository }}/${{matrix.app}}" -# - name: Build & Push Image -# if: startsWith(github.ref, 'refs/heads/release') -# run: | -# branch_name=${GITHUB_REF#refs/heads/} -# version_string="v${branch_name#release-}-nightly" -# -# docker build --build-arg APP=${{matrix.app}} -f ${{matrix.dockerFile}} . -t ghcr.io/kloudlite/web/${{matrix.app}}:$version_string --push -# docker build --build-arg APP=${{matrix.app}} -f ${{matrix.dockerFile}} . -t ghcr.io/kloudlite/web/${{matrix.app}}:commit-${GITHUB_SHA} --push \ No newline at end of file + docker manifest inspect $image_name:$IMAGE_TAG + exit_status=$? + if [ $exit_status -eq 0 ]; then + [ "$OVERRIDE_PUSHED_IMAGE" = "false" ] && echo "image ($image_name:$IMAGE_TAG) already exists, and override image is disable, exiting" && exit 0 + echo "image exists, but override pushed image is set to true. proceeding with building image" + fi + + set -e + + docker build --build-arg APP=${{matrix.app}} -f ${{matrix.dockerFile}} . -t "$image_name:$IMAGE_TAG" From 1389e693fd62c4790e400f346277dc971f41c283 Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Thu, 8 Feb 2024 15:50:49 +0530 Subject: [PATCH 3/7] :bookmark: Updated build --- .github/workflows/build-container.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-container.yaml b/.github/workflows/build-container.yaml index 970329047..c24b2ccfc 100644 --- a/.github/workflows/build-container.yaml +++ b/.github/workflows/build-container.yaml @@ -75,8 +75,8 @@ jobs: echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV echo "OVERRIDE_PUSHED_IMAGE=false" >> $GITHUB_ENV - - name: Build & Push Image - if: startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/') + - name: Build + if: "!startsWith(github.ref, 'refs/heads/release-') && !startsWith(github.ref, 'refs/tags/')" run: | set +e image_name="ghcr.io/${{ github.repository }}/${{matrix.app}}" @@ -90,10 +90,10 @@ jobs: set -e - docker build --build-arg APP=${{matrix.app}} -f ${{matrix.dockerFile}} . -t "$image_name:$IMAGE_TAG" --push + docker build --build-arg APP=${{matrix.app}} -f ${{matrix.dockerFile}} . -t "$image_name:$IMAGE_TAG" - name: Build & Push Image - if: !(startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/')) + if: startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/') run: | set +e image_name="ghcr.io/${{ github.repository }}/${{matrix.app}}" @@ -107,4 +107,5 @@ jobs: set -e - docker build --build-arg APP=${{matrix.app}} -f ${{matrix.dockerFile}} . -t "$image_name:$IMAGE_TAG" + docker build --build-arg APP=${{matrix.app}} -f ${{matrix.dockerFile}} . -t "$image_name:$IMAGE_TAG" --push + From ead116bec2a4e7992eb65438851d6637d2589523 Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Thu, 8 Feb 2024 15:53:20 +0530 Subject: [PATCH 4/7] :bookmark: Build update --- .github/workflows/build-container.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-container.yaml b/.github/workflows/build-container.yaml index c24b2ccfc..be915ccc5 100644 --- a/.github/workflows/build-container.yaml +++ b/.github/workflows/build-container.yaml @@ -81,14 +81,14 @@ jobs: set +e image_name="ghcr.io/${{ github.repository }}/${{matrix.app}}" - docker manifest inspect $image_name:$IMAGE_TAG + docker manifest inspect $image_name:test exit_status=$? if [ $exit_status -eq 0 ]; then [ "$OVERRIDE_PUSHED_IMAGE" = "false" ] && echo "image ($image_name:$IMAGE_TAG) already exists, and override image is disable, exiting" && exit 0 echo "image exists, but override pushed image is set to true. proceeding with building image" fi - set -e + set - docker build --build-arg APP=${{matrix.app}} -f ${{matrix.dockerFile}} . -t "$image_name:$IMAGE_TAG" From c2f7811c47801641b7e87bb984fa480dc533a66f Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Thu, 8 Feb 2024 15:55:16 +0530 Subject: [PATCH 5/7] :bookmark: Build update --- .github/workflows/build-container.yaml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/.github/workflows/build-container.yaml b/.github/workflows/build-container.yaml index be915ccc5..83f030dff 100644 --- a/.github/workflows/build-container.yaml +++ b/.github/workflows/build-container.yaml @@ -78,19 +78,8 @@ jobs: - name: Build if: "!startsWith(github.ref, 'refs/heads/release-') && !startsWith(github.ref, 'refs/tags/')" run: | - set +e image_name="ghcr.io/${{ github.repository }}/${{matrix.app}}" - - docker manifest inspect $image_name:test - exit_status=$? - if [ $exit_status -eq 0 ]; then - [ "$OVERRIDE_PUSHED_IMAGE" = "false" ] && echo "image ($image_name:$IMAGE_TAG) already exists, and override image is disable, exiting" && exit 0 - echo "image exists, but override pushed image is set to true. proceeding with building image" - fi - - set - - - docker build --build-arg APP=${{matrix.app}} -f ${{matrix.dockerFile}} . -t "$image_name:$IMAGE_TAG" + docker build --build-arg APP=${{matrix.app}} -f ${{matrix.dockerFile}} . -t "$image_name:test" - name: Build & Push Image if: startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/') From 2520c5c41c16fd081462aa1fbdc8996418a360a1 Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Thu, 8 Feb 2024 20:45:08 +0530 Subject: [PATCH 6/7] :art: Improved logs --- gql-queries-generator/doc/queries.graphql | 3 + lib/client/hooks/use-debounce.ts | 9 +- lib/types/common.ts | 2 + src/apps/console/components/code-view.tsx | 67 ++-- .../{logger.tsx => logger/index.tsx} | 206 +------------ .../components/logger/useSocketLogs.tsx | 256 ++++++++++++++++ .../console/page-components/app-states.tsx | 2 +- src/apps/console/root.tsx | 5 +- .../nodepools/nodepool-resources.tsx | 116 +++---- .../infra+/$cluster+/overview+/_layout.tsx | 3 +- .../infra+/$cluster+/overview+/info/route.tsx | 46 +-- .../overview+/logs-n-metrics/route.tsx | 287 ++++++++++++++++++ .../infra+/$cluster+/overview+/logs/route.tsx | 87 ------ .../$cluster+/overview+/metrics/route.tsx | 7 - .../$repo+/buildruns/buildruns-resources.tsx | 13 +- .../server/gql/queries/nodepool-queries.ts | 12 +- .../devdoc/app/components/highlightit.tsx | 2 +- src/generated/gql/sdl.graphql | 1 + src/generated/gql/server.ts | 3 + 19 files changed, 718 insertions(+), 409 deletions(-) rename src/apps/console/components/{logger.tsx => logger/index.tsx} (80%) create mode 100644 src/apps/console/components/logger/useSocketLogs.tsx create mode 100644 src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/logs-n-metrics/route.tsx delete mode 100644 src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/logs/route.tsx delete mode 100644 src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/metrics/route.tsx diff --git a/gql-queries-generator/doc/queries.graphql b/gql-queries-generator/doc/queries.graphql index 17b56733b..8aed3b80d 100644 --- a/gql-queries-generator/doc/queries.graphql +++ b/gql-queries-generator/doc/queries.graphql @@ -557,6 +557,7 @@ query consoleGetProviderSecret($name: String!) { query consoleGetNodePool($clusterName: String!, $poolName: String!) { infra_getNodePool(clusterName: $clusterName, poolName: $poolName) { + id clusterName createdBy { userEmail @@ -657,6 +658,7 @@ query consoleListNodePools($clusterName: String!, $search: SearchNodepool, $pagi edges { cursor node { + id clusterName createdBy { userEmail @@ -3786,6 +3788,7 @@ query authCli_getDevice($name: String!) { } clusterName projectName + environmentName spec { activeNamespace disabled diff --git a/lib/client/hooks/use-debounce.ts b/lib/client/hooks/use-debounce.ts index 9bfa35a0d..f8e9eff45 100644 --- a/lib/client/hooks/use-debounce.ts +++ b/lib/client/hooks/use-debounce.ts @@ -7,15 +7,22 @@ const useDebounce: IuseDebounce = (action, delay, dep = []) => { // const [debouncedValue, setDebouncedValue] = useState(value); useEffect( () => { + let resp = () => {}; // Update debounced value after delay const handler = setTimeout(() => { - action(); + resp = action(); }, delay); // Cancel the timeout if value changes (also on delay change or unmount) // This is how we prevent debounced value from updating if value is changed ... // .. within the delay period. Timeout gets cleared and restarted. return () => { clearTimeout(handler); + if (typeof resp === 'function') { + resp(); + return; + } + + console.log('resp', resp); }; }, [delay, ...dep] // Only re-call effect if value or delay changes diff --git a/lib/types/common.ts b/lib/types/common.ts index 2e99ee969..626caf76f 100644 --- a/lib/types/common.ts +++ b/lib/types/common.ts @@ -68,3 +68,5 @@ export type ExtractArrayType = T extends (infer U)[] ? U : never; export type LoaderResult Promise> = Awaited< ReturnType >; + +export type ISetState = React.Dispatch>; diff --git a/src/apps/console/components/code-view.tsx b/src/apps/console/components/code-view.tsx index bf91fa4c5..c7e70f125 100644 --- a/src/apps/console/components/code-view.tsx +++ b/src/apps/console/components/code-view.tsx @@ -1,4 +1,6 @@ import { CopySimple } from '@jengaicons/react'; +import hljs from 'highlight.js'; +import { useEffect, useRef } from 'react'; import { toast } from '~/components/molecule/toast'; import useClipboard from '~/root/lib/client/hooks/use-clipboard'; @@ -6,31 +8,60 @@ interface ICodeView { data: string; copy: boolean; showShellPrompt?: boolean; + language?: string; + title: string; } -const CodeView = ({ data, copy, showShellPrompt }: ICodeView) => { +const CodeView = ({ + data, + copy, + showShellPrompt, + language = 'shell', + title, +}: ICodeView) => { const { copy: cpy } = useClipboard({ onSuccess() { toast.success('Text copied to clipboard.'); }, }); + + const ref = useRef(null); + + useEffect(() => { + (async () => { + if (ref.current) { + const hr = hljs.highlight( + data, + { + language, + }, + false + ); + + // @ts-ignore + ref.current.innerHTML = hr.value; + } + })(); + }, [data, language]); + return ( -
{ - if (copy) cpy(data); - }} - className="group/sha cursor-pointer bg-surface-basic-active p-lg rounded-lg bodyMd flex flex-row gap-xl items-center" - > -
-        
-          {showShellPrompt && '$'}
-          
- {data} -
-
-
- - - +
+
{title}
+
+
{ + if (copy) cpy(data); + }} + className="group/sha cursor-pointer p-lg rounded-md bodyMd flex flex-row gap-xl items-center hljs w-full" + > +
+            {data}
+          
+ + + + +
+
); }; diff --git a/src/apps/console/components/logger.tsx b/src/apps/console/components/logger/index.tsx similarity index 80% rename from src/apps/console/components/logger.tsx rename to src/apps/console/components/logger/index.tsx index a7b7c1aa9..39429addc 100644 --- a/src/apps/console/components/logger.tsx +++ b/src/apps/console/components/logger/index.tsx @@ -7,32 +7,22 @@ import Fuse from 'fuse.js'; import hljs from 'highlight.js'; import React, { ReactNode, memo, useEffect, useRef, useState } from 'react'; import { ViewportList } from 'react-viewport-list'; -import * as sock from 'websocket'; import { dayjs } from '~/components/molecule/dayjs'; import { ISearchInfProps, useSearch, } from '~/root/lib/client/helpers/search-filter'; import useClass from '~/root/lib/client/hooks/use-class'; -import { socketUrl } from '~/root/lib/configs/base-url.cjs'; -import useDebounce from '~/root/lib/client/hooks/use-debounce'; -import { generatePlainColor } from './color-generator'; -import Pulsable from './pulsable'; -import { logsMockData } from '../dummy/data'; +import { generatePlainColor } from '../color-generator'; +import Pulsable from '../pulsable'; +import { logsMockData } from '../../dummy/data'; +import { ILog, ISocketMessage, IuseLog, useSocketLogs } from './useSocketLogs'; const hoverClass = `hover:bg-[#ddd]`; const hoverClassDark = `hover:bg-[#333]`; -type ILog = { - pod_name: string; - container_name: string; - message: string; - timestamp: string; -}; type ILogWithLineNumber = ILog & { lineNumber: number }; -type ISocketMessage = ILog; - const padLeadingZeros = (num: number, size: number) => { let s = `${num}`; while (s.length < size) s = `0${s}`; @@ -568,13 +558,6 @@ const LogBlock = ({ ); }; -interface IuseLog { - url?: string; - account: string; - cluster: string; - trackingId: string; -} - export interface IHighlightJsLog { websocket: IuseLog; follow?: boolean; @@ -598,185 +581,6 @@ export interface IHighlightJsLog { dark?: boolean; } -const useSocketLogs = ({ url, account, cluster, trackingId }: IuseLog) => { - const [logs, setLogs] = useState([]); - const [error, setError] = useState(''); - const [isLoading, setIsLoading] = useState(true); - - let wsclient: Promise; - - const [socState, setSocState] = useState(null); - const [subscribed, setSubscribed] = useState(false); - - if (typeof window !== 'undefined') { - try { - wsclient = new Promise((res, rej) => { - let rejected = false; - try { - // eslint-disable-next-line new-cap - const w = new sock.w3cwebsocket( - url || `${socketUrl}/logs`, - '', - '', - {} - ); - - w.onmessage = (msg) => { - try { - const m: { - timestamp: string; - message: string; - spec: { - podName: string; - containerName: string; - }; - type: 'update' | 'error' | 'info'; - } = JSON.parse(msg.data as string); - - if (m.type === 'error') { - setLogs([]); - console.error(m.message); - return; - } - - if (m.type === 'info') { - if (m.message === 'subscribed to logs') { - setSubscribed(true); - } - console.log(m.message); - return; - } - - if (m.type === 'update') { - console.log(m.message); - return; - } - - if (m.type === 'log') { - setIsLoading(false); - setLogs((s) => [ - ...s, - { - pod_name: m.spec.podName, - container_name: m.spec.containerName, - message: m.message, - timestamp: m.timestamp, - }, - ]); - return; - } - - console.log(m); - } catch (err) { - console.error(err); - } - }; - - w.onopen = () => { - res(w); - }; - - w.onerror = (e) => { - console.error(e); - if (!rejected) { - rejected = true; - rej(e); - } - }; - - w.onclose = () => {}; - } catch (e) { - rej(e); - } - }); - } catch (e) { - console.log(e); - } - } - - useDebounce( - () => { - (async () => { - try { - if (account === '' || cluster === '' || trackingId === '') { - return () => {}; - } - - if (logs.length) { - setLogs([]); - } - setIsLoading(true); - - const client = await wsclient; - - setSocState(client); - - client.send( - JSON.stringify({ - event: 'subscribe', - data: { - account, - cluster, - trackingId, - }, - }) - ); - } catch (e) { - console.error(e); - setLogs([]); - setError((e as Error).message); - } - - return () => { - (async () => { - if (!socState) return; - - socState.send( - JSON.stringify({ - event: 'unsubscribe', - data: { - account, - cluster, - trackingId, - }, - }) - ); - - // client.close(); - - setLogs([]); - })(); - }; - })(); - }, - 1000, - [account, cluster, trackingId, url] - ); - - useEffect(() => { - const sorted = logs.sort((a, b) => { - const resp = a.pod_name.localeCompare(b.pod_name); - - if (resp === 0) { - return dayjs(a.timestamp).unix() - dayjs(b.timestamp).unix(); - } - - return resp; - }); - - if (JSON.stringify(sorted) !== JSON.stringify(logs)) { - setLogs(sorted); - } - }, [logs]); - - return { - logs, - error, - isLoading, - subscribed, - }; -}; - const LogComp = ({ websocket, follow = true, @@ -837,7 +641,7 @@ const LogComp = ({
void; + subscribed: boolean; +}>({ + sock: null, + logs: [], + resetLogs: () => {}, + subscribed: false, +}); + +export interface IuseLog { + url?: string; + account: string; + cluster: string; + trackingId: string; +} + +const useLogsContext = () => { + return useContext(LogsContext); +}; + +export const LogsProvider = ({ + children, + url, +}: ChildrenProps & { + url?: string; +}) => { + const [logs, setLogs] = useState([]); + const [subscription, setSubscription] = useState<{ + account: string; + cluster: string; + trackingId: string; + } | null>(null); + + const [sock, setSock] = useState(null); + const sockPromise = useRef | null>(null); + const [subscribed, setSubscribed] = useState(false); + + useEffect(() => { + if (typeof window !== 'undefined') { + try { + sockPromise.current = new Promise((res, rej) => { + let rejected = false; + try { + // eslint-disable-next-line new-cap + const w = new wsock.w3cwebsocket( + url || `${socketUrl}/logs`, + '', + '', + {} + ); + + w.onmessage = (msg) => { + try { + const m: { + timestamp: string; + message: string; + spec: { + podName: string; + containerName: string; + }; + type: 'update' | 'error' | 'info'; + } = JSON.parse(msg.data as string); + + if (m.type === 'error') { + setLogs([]); + console.error(m.message); + return; + } + + if (m.type === 'info') { + if (m.message === 'subscribed to logs') { + setSubscribed(true); + } + console.log(m.message); + return; + } + + if (m.type === 'update') { + console.log(m.message); + return; + } + + if (m.type === 'log') { + // setIsLoading(false); + setLogs((s) => [ + ...s, + { + pod_name: m.spec.podName, + container_name: m.spec.containerName, + message: m.message, + timestamp: m.timestamp, + }, + ]); + return; + } + + console.log(m); + } catch (err) { + console.error(err); + } + }; + + w.onopen = () => { + res(w); + }; + + w.onerror = (e) => { + console.error(e); + if (!rejected) { + rejected = true; + rej(e); + } + }; + + w.onclose = () => {}; + } catch (e) { + rej(e); + } + }); + } catch (e) { + console.log(e); + } + } + }, []); + + useEffect(() => { + const sorted = logs.sort((a, b) => { + const resp = a.pod_name.localeCompare(b.pod_name); + + if (resp === 0) { + return dayjs(a.timestamp).unix() - dayjs(b.timestamp).unix(); + } + + return resp; + }); + + if (JSON.stringify(sorted) !== JSON.stringify(logs)) { + setLogs(sorted); + } + }, [logs]); + + useEffect(() => { + (async () => { + if (sockPromise.current) { + const resp = await sockPromise.current; + setSock(resp); + } + })(); + }, [sockPromise.current]); + + return ( + { + return { + sock, + logs, + resetLogs: () => { + setLogs([]); + }, + subscription, + setSubscription, + subscribed, + }; + }, [sock, logs])} + > + {children} + + ); +}; + +export const useSocketLogs = ({ account, cluster, trackingId }: IuseLog) => { + const [error, setError] = useState(''); + const [isLoading, setIsLoading] = useState(true); + + const { sock, subscribed, logs, resetLogs } = useLogsContext(); + + useDebounce( + () => { + try { + if (account === '' || cluster === '' || trackingId === '') { + return () => {}; + } + if (logs.length) { + resetLogs(); + } + + setIsLoading(true); + + sock?.send( + JSON.stringify({ + event: 'subscribe', + data: { + account, + cluster, + trackingId, + }, + }) + ); + } catch (e) { + console.error(e); + resetLogs(); + setError((e as Error).message); + } + + return () => { + sock?.send( + JSON.stringify({ + event: 'unsubscribe', + data: { + account, + cluster, + trackingId, + }, + }) + ); + + resetLogs(); + }; + }, + 1000, + [account, cluster, trackingId, sock] + ); + + return { + logs, + error, + isLoading, + subscribed, + }; +}; diff --git a/src/apps/console/page-components/app-states.tsx b/src/apps/console/page-components/app-states.tsx index 6a7d8c7ed..4ddce9c57 100644 --- a/src/apps/console/page-components/app-states.tsx +++ b/src/apps/console/page-components/app-states.tsx @@ -23,7 +23,7 @@ const defaultApp: AppIn = { displayName: '', }; -type ISetState = (fn: ((val: T) => T) | T) => void; +export type ISetState = (fn: ((val: T) => T) | T) => void; type ISetContainer = (fn: ((val: T) => T) | T, index?: number) => void; const CreateAppContext = createContext(null); diff --git a/src/apps/console/root.tsx b/src/apps/console/root.tsx index b28c2368d..af1ee71c6 100644 --- a/src/apps/console/root.tsx +++ b/src/apps/console/root.tsx @@ -4,6 +4,7 @@ import { ChildrenProps } from '~/components/types'; import authStylesUrl from './styles/index.css'; import highlightCss from './styles/hljs/tokyo-night-dark.min.css'; import { DataContextProvider } from './page-components/common-state'; +import { LogsProvider } from './components/logger/useSocketLogs'; export { loader } from '~/lib/app-setup/root.jsx'; export { shouldRevalidate } from '~/lib/app-setup/root.jsx'; @@ -26,7 +27,9 @@ const Layout = ({ children }: ChildrenProps) => { const _Root = ({ ...props }) => { return ( - + + + ); }; diff --git a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/nodepool-resources.tsx b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/nodepool-resources.tsx index 8104d51a9..f40b81587 100644 --- a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/nodepool-resources.tsx +++ b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/nodepools/nodepool-resources.tsx @@ -1,4 +1,4 @@ -import { PencilLine, Trash, Cpu, CircleFill } from '@jengaicons/react'; +import { PencilLine, Trash, Cpu } from '@jengaicons/react'; import { generateKey, titleCase } from '~/components/utils'; import ConsoleAvatar from '~/console/components/console-avatar'; import { @@ -25,11 +25,17 @@ import { handleError } from '~/root/lib/utils/common'; import { toast } from '~/components/molecule/toast'; import { useReload } from '~/root/lib/client/helpers/reloader'; import { useConsoleApi } from '~/console/server/gql/api-provider'; -import { Link } from '@remix-run/react'; +import { Link, useOutletContext } from '@remix-run/react'; import { IStatus, listRender } from '~/console/components/commons'; import { listStatus } from '~/console/components/sync-status'; +import AnimateHide from '~/components/atoms/animate-hide'; +import LogComp from '~/console/components/logger'; +import { ISetState } from '~/console/page-components/app-states'; +import { Button } from '~/components/atoms/button'; +import { dayjs } from '~/components/molecule/dayjs'; import HandleNodePool from './handle-nodepool'; import { findNodePlanWithCategory } from './nodepool-utils'; +import { IAccountContext } from '../../../_layout'; const RESOURCE_NAME = 'nodepool'; type BaseType = ExtractNodeType; @@ -99,14 +105,20 @@ const NodePoolAvatar = ({ title }: { title: string }) => { }; const ListDetail = ( - props: Omit & { open: boolean; item: BaseType } + props: Omit & { + open: string; + item: BaseType; + setOpen: ISetState; + } ) => { - const { item, open, onDelete, onEdit } = props; + const { item, onDelete, onEdit, open, setOpen } = props; const { name, id } = parseItem(item); const { minCount, maxCount, cloudProvider, aws } = item.spec; const keyPrefix = `${RESOURCE_NAME}-${id}`; const lR = listRender({ keyPrefix, resource: item }); + const { account } = useOutletContext(); + const parseSize = () => { if (minCount === maxCount) { return ( @@ -164,6 +176,8 @@ const ListDetail = ( className: 'basis-full text-center', }); + const isLatest = dayjs(item.updateTime).isAfter(dayjs().subtract(3, 'hour')); + return (
@@ -173,13 +187,30 @@ const ListDetail = ( subtitle={
{id} - {/**/} - {/*Running {targetCount} nodes*/} + {/* */} + {/* Running {targetCount} nodes */}
} avatar={} />
+ + {isLatest && ( +
- {/*
-
- Expected - - {targetCount} node - {targetCount > 1 && 's'} - -
-
- Current - - {targetCount} node - {targetCount > 1 && 's'} - -
-
*/}
{lR.authorRender({ className: '' }).render()}
+ onDelete(item)} onEdit={() => onEdit(item)} + // onLogsToggle={() => { + // setOpen((s) => { + // if (s === item.id) { + // return ''; + // } + // return item.id; + // }); + // }} status={statusRender.status} />
- {/* hello */} + + +
); }; @@ -295,7 +334,7 @@ const GridView = ({ items, onDelete, onEdit }: IResource) => { }; const ListView = ({ items, onDelete, onEdit }: IResource) => { - const [open, setOpen] = useState(null); + const [open, setOpen] = useState(''); return ( {items.map((item, index) => { @@ -306,32 +345,6 @@ const ListView = ({ items, onDelete, onEdit }: IResource) => { setOpen((prev) => (prev === id ? null : id))} - // columns={[ - // { - // key: generateKey(keyPrefix, name + id), - // className: 'flex-1', - // render: () => ( - // } - // /> - // ), - // }, - // lR.statusRender({ className: 'w-[180px]' }), - // lR.authorRender({ className: 'w-[180px]' }), - // { - // key: generateKey(keyPrefix, 'action'), - // render: () => ( - // onDelete(item)} - // onEdit={() => onEdit(item)} - // onShowResourceYaml={() => onShowResourceYaml(item)} - // /> - // ), - // }, - // ]} columns={[ { className: 'w-full', @@ -339,7 +352,8 @@ const ListView = ({ items, onDelete, onEdit }: IResource) => { render: () => ( onDelete(item)} onEdit={() => onEdit(item)} /> diff --git a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/_layout.tsx b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/_layout.tsx index db675c12d..45554775f 100644 --- a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/_layout.tsx +++ b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/_layout.tsx @@ -11,8 +11,7 @@ const ClusterOverview = () => { { }; const ClusterInfo = () => { - const { cluster, account } = useOutletContext(); + const { cluster } = useOutletContext(); const providerInfo = () => { const provider = cluster.spec?.cloudProvider; switch (provider) { @@ -176,31 +176,31 @@ const ClusterInfo = () => { Log into your account and start accessing your cluster config using - kl clustercommand as shown below. + klicommand as shown below.
- - } + + - - } + + + + + ); diff --git a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/logs-n-metrics/route.tsx b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/logs-n-metrics/route.tsx new file mode 100644 index 000000000..0480dcedd --- /dev/null +++ b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/logs-n-metrics/route.tsx @@ -0,0 +1,287 @@ +import { useOutletContext } from '@remix-run/react'; +import Chart from '~/console/components/charts/charts-client'; +import { useState } from 'react'; +import { dayjs } from '~/components/molecule/dayjs'; +import { ApexOptions } from 'apexcharts'; +import LogComp from '~/console/components/logger'; +import { parseName } from '~/console/server/r-utils/common'; +import { Clock, ListNumbers } from '@jengaicons/react'; +import { cn } from '~/components/utils'; +import { useDataState } from '~/console/page-components/common-state'; +import { IClusterContext } from '../../_layout'; + +const LogsAndMetrics = () => { + const { cluster, account } = useOutletContext(); + const [cpuData, _setCpuData] = useState([]); + const [memoryData, _setMemoryData] = useState([]); + + const xAxisFormatter = (_: string, __?: number) => { + // return dayjs((val || 0) * 1000).format('hh:mm A'); + return ''; + }; + + const tooltipXAixsFormatter = (val: number) => + dayjs(val * 1000).format('DD/MM/YY hh:mm A'); + + // useDebounce( + // () => { + // (async () => { + // try { + // const resp = await axios({ + // url: `https://observe.dev.kloudlite.io/observability/metrics/cpu?cluster_name=${parseName( + // cluster + // )}&tracking_id=${cluster.id}`, + // method: 'GET', + // withCredentials: true, + // }); + // + // setCpuData(resp?.data?.data?.result[0]?.values || []); + // } catch (err) { + // console.error(err); + // } + // })(); + // (async () => { + // try { + // const resp = await axios({ + // url: `https://observe.dev.kloudlite.io/observability/metrics/memory?cluster_name=${parseName( + // cluster + // )}&tracking_id=${cluster.id}`, + // method: 'GET', + // withCredentials: true, + // }); + // + // setMemoryData(resp?.data?.data?.result[0]?.values || []); + // } catch (err) { + // console.error(err); + // } + // })(); + // }, + // 1000, + // [] + // ); + + const chartOptions: ApexOptions = { + chart: { + type: 'area', + zoom: { + enabled: false, + }, + toolbar: { + show: false, + }, + redrawOnWindowResize: true, + }, + dataLabels: { + enabled: false, + }, + stroke: { + curve: 'smooth', + }, + + xaxis: { + type: 'datetime', + labels: { + show: false, + formatter: xAxisFormatter, + }, + }, + }; + + const { state, setState } = useDataState<{ + linesVisible: boolean; + timestampVisible: boolean; + }>('logs'); + + return ( +
+
+ `${val} m`, + }, + }, + }} + /> + + `${val} MB`, + }, + }, + tooltip: { + x: { + formatter: tooltipXAixsFormatter, + }, + y: { + formatter(val) { + return `${val.toFixed(2)} MB`; + }, + }, + }, + }} + /> + + `${val} MB`, + }, + }, + tooltip: { + x: { + formatter: tooltipXAixsFormatter, + }, + y: { + formatter(val) { + return `${val.toFixed(2)} MB`; + }, + }, + }, + }} + /> + + `${val} MB`, + }, + }, + tooltip: { + x: { + formatter: tooltipXAixsFormatter, + }, + y: { + formatter(val) { + return `${val.toFixed(2)} MB`; + }, + }, + }, + }} + /> +
+ +
+ +
{ + setState((s) => ({ ...s, linesVisible: !s.linesVisible })); + }} + className="flex items-center justify-center font-bold text-xl cursor-pointer select-none active:translate-y-[1px] transition-all" + > + + + +
+
{ + setState((s) => ({ + ...s, + timestampVisible: !s.timestampVisible, + })); + }} + className="flex items-center justify-center font-bold text-xl cursor-pointer select-none active:translate-y-[1px] transition-all" + > + + + +
+
+ ), + websocket: { + account: parseName(account), + cluster: parseName(cluster), + trackingId: cluster.id, + }, + }} + /> +
+ + ); +}; + +export default LogsAndMetrics; diff --git a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/logs/route.tsx b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/logs/route.tsx deleted file mode 100644 index 777dac184..000000000 --- a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/logs/route.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { Suspense } from 'react'; -import { Box } from '~/console/components/common-console-components'; -import LogComp from '~/console/components/logger'; - -const Log = () => { - const getTime = () => { - return Math.floor(new Date().getTime() / 1000); - }; - - const selectOptions = [ - { - label: 'Last 12 hours', - value: '1', - from: () => getTime() - 43200, - }, - { - label: 'Last 24 hours', - value: '2', - from: () => getTime() - 86400, - }, - { - label: 'Last 7 days', - value: '3', - from: () => getTime() - 604800, - }, - { - label: 'Last 30 days', - value: '3', - from: () => getTime() - 2592000, - }, - ]; - - // const [from, setFrom] = useState(selectOptions[1].from()); - // const [selected, setSelected] = useState('1'); - - const getUrl = (f: number) => { - return `wss://observability.dev.kloudlite.io/observability/logs/cluster-job?start_time=${f}&end_time=${getTime()}`; - }; - - // const [url, setUrl] = useState(getUrl(from)); - - return ( - -
- selectOptions} - // value={selectOptions[parseValue(selected, 1)]} - // onChange={(e) => { - // setSelected(e.value); - // }} - // /> - // } - // title={ - //
- //
Cluster logs
- //
- // } - dark - websocket={{ - account: '', - trackingId: '', - cluster: '', - }} - height="60vh" - width="100%" - url={getUrl(selectOptions[3].from())} - selectableLines - /> -
-
- ); -}; - -const ClusterLogs = () => { - return ( -
- - - -
- ); -}; - -export default ClusterLogs; diff --git a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/metrics/route.tsx b/src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/metrics/route.tsx deleted file mode 100644 index 898af67db..000000000 --- a/src/apps/console/routes/_main+/$account+/infra+/$cluster+/overview+/metrics/route.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { Box } from '~/console/components/common-console-components'; - -const ClusterMetrics = () => { - return Metrics; -}; - -export default ClusterMetrics; diff --git a/src/apps/console/routes/_main+/$account+/repo+/$repo+/buildruns/buildruns-resources.tsx b/src/apps/console/routes/_main+/$account+/repo+/$repo+/buildruns/buildruns-resources.tsx index b1031a5f6..22923be5f 100644 --- a/src/apps/console/routes/_main+/$account+/repo+/$repo+/buildruns/buildruns-resources.tsx +++ b/src/apps/console/routes/_main+/$account+/repo+/$repo+/buildruns/buildruns-resources.tsx @@ -1,6 +1,5 @@ import { useState } from 'react'; -import { toast } from '~/components/molecule/toast'; -import { cn, generateKey, titleCase } from '~/components/utils'; +import { cn, generateKey } from '~/components/utils'; import { ListBody, ListTitle, @@ -9,15 +8,12 @@ import DeleteDialog from '~/console/components/delete-dialog'; import Grid from '~/console/components/grid'; import List from '~/console/components/list'; import ListGridView from '~/console/components/list-grid-view'; -import { useConsoleApi } from '~/console/server/gql/api-provider'; import { ExtractNodeType, parseName, parseUpdateOrCreatedOn, } from '~/console/server/r-utils/common'; -import { useReload } from '~/root/lib/client/helpers/reloader'; -import { handleError } from '~/root/lib/utils/common'; -import { useOutletContext, useParams } from '@remix-run/react'; +import { useOutletContext } from '@remix-run/react'; import { IBuildRuns } from '~/console/server/gql/queries/build-run-queries'; import AnimateHide from '~/components/atoms/animate-hide'; import LogComp from '~/console/components/logger'; @@ -30,7 +26,6 @@ import { XCircleFill, } from '@jengaicons/react'; import dayjs from 'dayjs'; -import { Badge } from '~/components/atoms/badge'; import { IAccountContext } from '../../../_layout'; const RESOURCE_NAME = 'build run'; @@ -234,9 +229,6 @@ const BuildRunResources = ({ items = [] }: { items: BaseType[] }) => { null ); - const api = useConsoleApi(); - const reloadPage = useReload(); - const props: IResource = { items, // onDelete: (item) => { @@ -244,7 +236,6 @@ const BuildRunResources = ({ items = [] }: { items: BaseType[] }) => { // }, }; - const params = useParams(); return ( <> ({ gql` query Infra_getNodePool($clusterName: String!, $poolName: String!) { infra_getNodePool(clusterName: $clusterName, poolName: $poolName) { + id clusterName createdBy { userEmail @@ -103,7 +104,7 @@ export const nodepoolQueries = (executor: IExecutor) => ({ transformer(data: ConsoleGetNodePoolQuery) { return data.infra_getNodePool; }, - vars(_: ConsoleGetNodePoolQueryVariables) { }, + vars(_: ConsoleGetNodePoolQueryVariables) {}, } ), createNodePool: executor( @@ -117,7 +118,7 @@ export const nodepoolQueries = (executor: IExecutor) => ({ { transformer: (data: ConsoleCreateNodePoolMutation) => data.infra_createNodePool, - vars(_: ConsoleCreateNodePoolMutationVariables) { }, + vars(_: ConsoleCreateNodePoolMutationVariables) {}, } ), updateNodePool: executor( @@ -131,7 +132,7 @@ export const nodepoolQueries = (executor: IExecutor) => ({ { transformer: (data: ConsoleCreateNodePoolMutation) => data.infra_createNodePool, - vars(_: ConsoleCreateNodePoolMutationVariables) { }, + vars(_: ConsoleCreateNodePoolMutationVariables) {}, } ), listNodePools: executor( @@ -149,6 +150,7 @@ export const nodepoolQueries = (executor: IExecutor) => ({ edges { cursor node { + id clusterName createdBy { userEmail @@ -239,7 +241,7 @@ export const nodepoolQueries = (executor: IExecutor) => ({ { transformer: (data: ConsoleListNodePoolsQuery) => data.infra_listNodePools, - vars(_: ConsoleListNodePoolsQueryVariables) { }, + vars(_: ConsoleListNodePoolsQueryVariables) {}, } ), deleteNodePool: executor( @@ -251,7 +253,7 @@ export const nodepoolQueries = (executor: IExecutor) => ({ { transformer: (data: ConsoleDeleteNodePoolMutation) => data.infra_deleteNodePool, - vars(_: ConsoleDeleteNodePoolMutationVariables) { }, + vars(_: ConsoleDeleteNodePoolMutationVariables) {}, } ), }); diff --git a/src/apps/devdoc/app/components/highlightit.tsx b/src/apps/devdoc/app/components/highlightit.tsx index 86ad305b3..f051e2c10 100644 --- a/src/apps/devdoc/app/components/highlightit.tsx +++ b/src/apps/devdoc/app/components/highlightit.tsx @@ -18,7 +18,7 @@ const HighlightIt = ({ if (ref.current) { // @ts-ignore ref.current.innerHTML = hljs.highlight( - inlineData, + `
${inlineData}
`, { language, }, diff --git a/src/generated/gql/sdl.graphql b/src/generated/gql/sdl.graphql index da4b64fbf..c202c8c67 100644 --- a/src/generated/gql/sdl.graphql +++ b/src/generated/gql/sdl.graphql @@ -2920,6 +2920,7 @@ type Mutation { infra_deleteHelmRelease(clusterName: String!, releaseName: String!): Boolean! infra_deleteNodePool(clusterName: String!, poolName: String!): Boolean! infra_deleteProviderSecret(secretName: String!): Boolean! + infra_deletePV(clusterName: String!, pvName: String!): Boolean! infra_updateCluster(cluster: ClusterIn!): Cluster infra_updateClusterManagedService(clusterName: String!, service: ClusterManagedServiceIn!): ClusterManagedService infra_updateDomainEntry(domainEntry: DomainEntryIn!): DomainEntry diff --git a/src/generated/gql/server.ts b/src/generated/gql/server.ts index b979d299a..da413414b 100644 --- a/src/generated/gql/server.ts +++ b/src/generated/gql/server.ts @@ -2012,6 +2012,7 @@ export type ConsoleGetNodePoolQueryVariables = Exact<{ export type ConsoleGetNodePoolQuery = { infra_getNodePool?: { + id: string; clusterName: string; creationTime: any; displayName: string; @@ -2100,6 +2101,7 @@ export type ConsoleListNodePoolsQuery = { edges: Array<{ cursor: string; node: { + id: string; clusterName: string; creationTime: any; displayName: string; @@ -5045,6 +5047,7 @@ export type AuthCli_GetDeviceQuery = { displayName: string; clusterName?: string; projectName?: string; + environmentName?: string; metadata?: { name: string }; spec?: { activeNamespace?: string; From f601b8b103407443d6a80e248db181dea028ac95 Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Thu, 8 Feb 2024 20:59:56 +0530 Subject: [PATCH 7/7] :art: Logs improved --- src/apps/console/components/logger/useSocketLogs.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/apps/console/components/logger/useSocketLogs.tsx b/src/apps/console/components/logger/useSocketLogs.tsx index 683448b41..4ae0584a9 100644 --- a/src/apps/console/components/logger/useSocketLogs.tsx +++ b/src/apps/console/components/logger/useSocketLogs.tsx @@ -26,11 +26,13 @@ const LogsContext = createContext<{ logs: ISocketMessage[]; resetLogs: () => void; subscribed: boolean; + setSubscribed: (s: boolean) => void; }>({ sock: null, logs: [], resetLogs: () => {}, subscribed: false, + setSubscribed: () => {}, }); export interface IuseLog { @@ -186,6 +188,7 @@ export const LogsProvider = ({ subscription, setSubscription, subscribed, + setSubscribed, }; }, [sock, logs])} > @@ -198,7 +201,7 @@ export const useSocketLogs = ({ account, cluster, trackingId }: IuseLog) => { const [error, setError] = useState(''); const [isLoading, setIsLoading] = useState(true); - const { sock, subscribed, logs, resetLogs } = useLogsContext(); + const { sock, subscribed, logs, resetLogs, setSubscribed } = useLogsContext(); useDebounce( () => { @@ -229,6 +232,7 @@ export const useSocketLogs = ({ account, cluster, trackingId }: IuseLog) => { } return () => { + setSubscribed(false); sock?.send( JSON.stringify({ event: 'unsubscribe',