diff --git a/packages/management-api/src/managed-pod.ts b/packages/management-api/src/managed-pod.ts index adea2a94..7bf53300 100644 --- a/packages/management-api/src/managed-pod.ts +++ b/packages/management-api/src/managed-pod.ts @@ -13,6 +13,7 @@ const DEFAULT_JOLOKIA_OPTIONS: BaseRequestOptions = { export type Management = { status: { + managed: boolean running: boolean error: boolean } @@ -30,6 +31,7 @@ export class ManagedPod { private _management: Management = { status: { + managed: false, running: false, error: false, }, diff --git a/packages/management-api/src/management-service.ts b/packages/management-api/src/management-service.ts index 323d5ef2..1c44e98b 100644 --- a/packages/management-api/src/management-service.ts +++ b/packages/management-api/src/management-service.ts @@ -4,6 +4,11 @@ import { Connection, Connections, connectService } from '@hawtio/react' import { k8Service, k8Api, KubePod, K8Actions, Container, ContainerPort, debounce } from '@hawtio/online-kubernetes-api' import { MgmtActions, log } from './globals' +interface UpdateEmitter { + uid?: string + fireUpdate: boolean +} + export class ManagementService extends EventEmitter { private _initialized = false private _pods: { [key: string]: ManagedPod } = {} @@ -79,10 +84,12 @@ export class ManagementService extends EventEmitter { Object.keys(this._pods).forEach(uid => this.uidQueue.add(uid)) } - private emitUpdate(uid: string, fireUpdate: boolean) { - this.uidQueue.delete(uid) + private emitUpdate(emitter: UpdateEmitter) { + if (emitter.uid) { + this.uidQueue.delete(emitter.uid) + } - if (fireUpdate && this.uidQueue.size === 0) { + if (emitter.fireUpdate && this.uidQueue.size === 0) { this.emit(MgmtActions.UPDATED) } } @@ -90,10 +97,23 @@ export class ManagementService extends EventEmitter { private async mgmtUpdate() { this.preMgmtUpdate() + if (Object.keys(this._pods).length === 0) { + /* + * If there are no pods, we still want an update to fire + * to let 3rd parties know that updates are happening but + * currently there are no pods to report on + */ + this.emitUpdate({ fireUpdate: true }) + return + } + for (const uid of Object.keys(this._pods)) { const mPod: ManagedPod = this._pods[uid] const fingerprint = this.fingerprint(mPod.management) + // Flag that the pod is now under management + mPod.management.status.managed = true + mPod.management.status.running = this.podStatus(mPod) === 'Running' if (!mPod.management.status.running) { @@ -102,7 +122,7 @@ export class ManagementService extends EventEmitter { * against a non-running pod. * Emit an update but only if the status has in fact changed */ - this.emitUpdate(uid, fingerprint === this.fingerprint(mPod.management)) + this.emitUpdate({ uid, fireUpdate: fingerprint === this.fingerprint(mPod.management) }) continue } @@ -112,12 +132,12 @@ export class ManagementService extends EventEmitter { try { const url = await mPod.probeJolokiaUrl() if (!url) { - this.emitUpdate(uid, fingerprint === this.fingerprint(mPod.management)) + this.emitUpdate({ uid, fireUpdate: fingerprint === this.fingerprint(mPod.management) }) } } catch (error) { log.error(new Error(`Cannot access jolokia url at ${mPod.jolokiaPath}`, { cause: error })) mPod.management.status.error = true - this.emitUpdate(uid, fingerprint === this.fingerprint(mPod.management)) + this.emitUpdate({ uid, fireUpdate: fingerprint === this.fingerprint(mPod.management) }) continue } @@ -126,12 +146,12 @@ export class ManagementService extends EventEmitter { success: (routes: string[]) => { mPod.management.status.error = false mPod.management.camel.routes_count = routes.length - this.emitUpdate(uid, fingerprint === this.fingerprint(mPod.management)) + this.emitUpdate({ uid, fireUpdate: fingerprint === this.fingerprint(mPod.management) }) }, error: error => { log.error(error) mPod.management.status.error = true - this.emitUpdate(uid, fingerprint === this.fingerprint(mPod.management)) + this.emitUpdate({ uid, fireUpdate: fingerprint === this.fingerprint(mPod.management) }) }, }) } diff --git a/packages/online-shell/src/discover/Discover.css b/packages/online-shell/src/discover/Discover.css index 922576aa..2bdd168b 100644 --- a/packages/online-shell/src/discover/Discover.css +++ b/packages/online-shell/src/discover/Discover.css @@ -23,6 +23,16 @@ display: none; } +.discover-loading { + width: 85%; + text-align: center; + margin-left: auto; + margin-right: auto; + margin-top: 2em; + background-color: transparent; + font-style: italic; +} + .discover-group-label { text-align: left; color: rgb(117, 117, 117); diff --git a/packages/online-shell/src/discover/Discover.tsx b/packages/online-shell/src/discover/Discover.tsx index fa8bbdc6..ff6b79e8 100644 --- a/packages/online-shell/src/discover/Discover.tsx +++ b/packages/online-shell/src/discover/Discover.tsx @@ -9,6 +9,10 @@ import { List, PageSection, PageSectionVariants, + Panel, + PanelHeader, + PanelMain, + PanelMainBody, Title, } from '@patternfly/react-core' import { CubesIcon } from '@patternfly/react-icons' @@ -24,7 +28,18 @@ export const Discover: React.FunctionComponent = () => { useDisplayItems() if (isLoading) { - return + return ( + + + Waiting for Hawtio Containers ... + + + + + + + + ) } if (error) { @@ -43,15 +58,17 @@ export const Discover: React.FunctionComponent = () => { if (discoverGroups.length + discoverPods.length === 0) { return ( - - - - No Hawtio Containers - - - There are no containers running with a port configured whose name is jolokia. - - + + + + + No Hawtio Containers + + + There are no containers running with a port configured whose name is jolokia. + + + ) } diff --git a/packages/online-shell/src/discover/DiscoverPodItem.tsx b/packages/online-shell/src/discover/DiscoverPodItem.tsx index 7493ecc8..0dbc6f3b 100644 --- a/packages/online-shell/src/discover/DiscoverPodItem.tsx +++ b/packages/online-shell/src/discover/DiscoverPodItem.tsx @@ -1,6 +1,8 @@ import React, { ReactNode } from 'react' import { Label, LabelGroup, ListItem, Title } from '@patternfly/react-core' import { DatabaseIcon, HomeIcon, OutlinedHddIcon } from '@patternfly/react-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faSpinner } from '@fortawesome/free-solid-svg-icons' import { ConsoleLink, ConsoleType } from '../console' import { Labels } from '../labels' import { DiscoverPod } from './globals' @@ -32,8 +34,20 @@ export const DiscoverPodItem: React.FunctionComponent = (p } const routesLabel = (): ReactNode => { + if (!props.pod.mPod.management.status.managed) { + return ( + + ) + } + const total = props.pod.mPod.management.camel.routes_count - return `${total} route${total !== 1 ? 's' : ''}` + return ( + + ) } return ( @@ -68,9 +82,7 @@ export const DiscoverPodItem: React.FunctionComponent = (p {containersLabel()} - + {routesLabel()}
diff --git a/packages/online-shell/src/discover/context.ts b/packages/online-shell/src/discover/context.ts index cb804ad9..9c3f8920 100644 --- a/packages/online-shell/src/discover/context.ts +++ b/packages/online-shell/src/discover/context.ts @@ -11,6 +11,7 @@ type UpdateListener = () => void */ export function useDisplayItems() { const timerRef = useRef(null) + const [isMounting, setIsMounting] = useState(true) const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState() const [discoverGroups, setDiscoverGroups] = useState([]) @@ -27,22 +28,22 @@ export function useDisplayItems() { } useEffect(() => { - setIsLoading(true) + setIsMounting(true) const checkLoading = async () => { const mgmtLoaded = await isMgmtApiRegistered() if (!mgmtLoaded) return - setIsLoading(false) - if (k8Api.hasError()) { setError(k8Api.error) + setIsMounting(false) return } if (k8Service.hasError()) { setError(k8Service.error) + setIsMounting(false) return } @@ -50,6 +51,7 @@ export function useDisplayItems() { // First-time update pod organisation // organisePods([], []) + setIsMounting(false) } checkLoading() @@ -71,6 +73,8 @@ export function useDisplayItems() { useEffect(() => { const mgmtListener = () => { organisePods(discoverGroups, filters) + // Listener inited so loading now complete + setIsLoading(false) } mgmtService.on(MgmtActions.UPDATED, mgmtListener) @@ -79,7 +83,7 @@ export function useDisplayItems() { return () => { if (updateListener.current) mgmtService.off(MgmtActions.UPDATED, updateListener.current) } - }, [isLoading, filters, discoverGroups]) + }, [isMounting, filters, discoverGroups]) return { error, isLoading, discoverGroups, setDiscoverGroups, discoverPods, setDiscoverPods, filters, setFilters } }