Skip to content

Commit

Permalink
Updates Kubernetes test app (hawtio#496)
Browse files Browse the repository at this point in the history
* Places the API Properties in a tab to give all tabs more space

* Adds Prev and Next buttons to pods tab to choose the next set of pods
  to display in the namespace

* Puts projects inside an Accordian to separate out the pods logically

* KubernetesPods -> KubernetesProjectPods

* KubernetesClient removed as folded into Kubernetes component
  • Loading branch information
phantomjinx committed Sep 25, 2024
1 parent 0393b5c commit 58ba7d6
Show file tree
Hide file tree
Showing 7 changed files with 340 additions and 193 deletions.
103 changes: 74 additions & 29 deletions packages/kubernetes-api-app/src/Kubernetes.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { isK8ApiRegistered, k8Api, k8Service } from '@hawtio/online-kubernetes-api'
import React, { useRef, useEffect, useState } from 'react'
import {
Alert,
Expand All @@ -20,17 +19,29 @@ import {
MastheadContent,
Label,
Button,
Tabs,
Tab,
TabTitleText,
} from '@patternfly/react-core'
import { InfoCircleIcon } from '@patternfly/react-icons'
import { KubernetesClient } from './KubernetesClient'
import { useUser, userService } from '@hawtio/react'
import {
K8Actions, isK8ApiRegistered, k8Api, k8Service,
KubeProject, KubePodsByProject
} from '@hawtio/online-kubernetes-api'
import { KubernetesProjectPods } from './KubernetesProjectPods'
import { KubernetesProjects } from './KubernetesProjects'

export const Kubernetes: React.FunctionComponent = () => {
const timerRef = useRef<NodeJS.Timeout | null>(null)
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<Error | null>()
const { username, userLoaded } = useUser()

const [projects, setProjects] = useState<KubeProject[]>([])
const [podsByProject, setPodsByProject] = useState<KubePodsByProject>({})
const [activeTabKey, setActiveTabKey] = React.useState<string | number>(0)

useEffect(() => {
setIsLoading(true)

Expand All @@ -50,7 +61,18 @@ export const Kubernetes: React.FunctionComponent = () => {

if (k8Service.hasError()) {
setError(k8Service.error)
return
}

k8Service.on(K8Actions.CHANGED, () => {
const projects = k8Service.getProjects()
setProjects([...projects]) // must use spread to ensure update

// Ensure that update is carried out on nested objects
// by using assign to create new object
const podsByProject: KubePodsByProject = k8Service.getPods()
setPodsByProject(Object.assign({}, podsByProject))
})
}

checkLoading()
Expand Down Expand Up @@ -93,6 +115,13 @@ export const Kubernetes: React.FunctionComponent = () => {
)
}

const handleTabClick = (
event: React.MouseEvent<unknown> | React.KeyboardEvent | MouseEvent,
tabIndex: string | number,
) => {
setActiveTabKey(tabIndex)
}

return (
<Card>
<CardTitle>
Expand All @@ -119,39 +148,55 @@ export const Kubernetes: React.FunctionComponent = () => {
</Masthead>

<Panel>
<PanelHeader>API Properties</PanelHeader>
<PanelHeader>
<Title headingLevel='h1'>Kubernetes Client</Title>
</PanelHeader>
<Divider />
<PanelMain>
<PanelMainBody>
<DescriptionList isHorizontal>
<DescriptionListGroup>
<DescriptionListTerm>Kubernetes Master</DescriptionListTerm>
<DescriptionListDescription>{k8Api.masterUri()}</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>Is Openshift?</DescriptionListTerm>
<DescriptionListDescription>{k8Api.isOpenshift ? 'true' : 'false'}</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>Cluster Console</DescriptionListTerm>
<DescriptionListDescription>
{k8Api.consoleUri ? <a href={k8Api.consoleUri}>{k8Api.consoleUri}</a> : '<not found>'}
</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>Kubernetes Config</DescriptionListTerm>
<DescriptionListDescription>
<pre>{JSON.stringify(k8Api.oAuthProfile, null, 2)}</pre>
</DescriptionListDescription>
</DescriptionListGroup>
</DescriptionList>
<Tabs activeKey={activeTabKey} onSelect={handleTabClick} isBox>
<Tab eventKey={0} title={<TabTitleText>API Properties</TabTitleText>}>
<Panel>
<PanelMain>
<PanelMainBody>
<DescriptionList isHorizontal>
<DescriptionListGroup>
<DescriptionListTerm>Kubernetes Master</DescriptionListTerm>
<DescriptionListDescription>{k8Api.masterUri()}</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>Is Openshift?</DescriptionListTerm>
<DescriptionListDescription>{k8Api.isOpenshift ? 'true' : 'false'}</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>Cluster Console</DescriptionListTerm>
<DescriptionListDescription>
{k8Api.consoleUri ? <a href={k8Api.consoleUri}>{k8Api.consoleUri}</a> : '<not found>'}
</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>Kubernetes Config</DescriptionListTerm>
<DescriptionListDescription>
<pre>{JSON.stringify(k8Api.oAuthProfile, null, 2)}</pre>
</DescriptionListDescription>
</DescriptionListGroup>
</DescriptionList>
</PanelMainBody>
</PanelMain>
</Panel>
</Tab>
<Tab eventKey={1} title={<TabTitleText>Pods</TabTitleText>}>
<KubernetesProjectPods podsByProject={podsByProject} />
</Tab>
{projects.length > 0 && (
<Tab eventKey={2} title={<TabTitleText>Projects</TabTitleText>}>
<KubernetesProjects projects={projects} />
</Tab>
)}
</Tabs>
</PanelMainBody>
</PanelMain>
</Panel>

<Divider />

<KubernetesClient />
</CardBody>
</Card>
)
Expand Down
52 changes: 0 additions & 52 deletions packages/kubernetes-api-app/src/KubernetesClient.tsx

This file was deleted.

125 changes: 125 additions & 0 deletions packages/kubernetes-api-app/src/KubernetesPaginatedPods.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React from 'react'
import {
Button,
DescriptionList,
DescriptionListDescription,
DescriptionListGroup,
DescriptionListTerm,
EmptyState,
EmptyStateBody,
EmptyStateVariant,
Panel,
PanelHeader,
PanelMain,
PanelMainBody,
Title,
Toolbar,
ToolbarContent,
ToolbarItem
} from '@patternfly/react-core'
import { Table, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'
import { KubePod, k8Service } from '@hawtio/online-kubernetes-api'

type KubePagePodsProps = {
project: string
pods: KubePod[]
}

export const KubernetesPaginatedPods: React.FunctionComponent<KubePagePodsProps> = (props: KubePagePodsProps) => {

const prevPods = () => {
// Should refresh from 2 components up
k8Service.previous(props.project)
}

const nextPods = () => {
// Should refresh from 2 components up
k8Service.next(props.project)
}

return (
<Panel isScrollable>
<PanelHeader>
<Toolbar id='pagination-toolbar-items' className='paginated-pods-toolbar-content' isSticky>
<ToolbarContent>
<ToolbarItem>
<Button variant='control' onClick={() => prevPods()}
isDisabled={! k8Service.hasPrevious(props.project)}
>
&lt;&lt; Previous
</Button>
</ToolbarItem>
<ToolbarItem>
<Button variant='control' onClick={() => nextPods()}
isDisabled={! k8Service.hasNext(props.project)}
>
Next &gt;&gt;
</Button>
</ToolbarItem>
</ToolbarContent>
</Toolbar>
</PanelHeader>
<PanelMain>
<PanelMainBody>
{props.pods.length === 0 && (
<EmptyState variant={EmptyStateVariant.xs}>
<Title headingLevel="h4" size="md">
No jolokia pods found
</Title>
<EmptyStateBody>
Pods were retrieved but none have a jolokia port.
</EmptyStateBody>
</EmptyState>
)}

{props.pods.length > 0 && (
<Table key={props.project} aria-label='Pods table' variant='compact'>
<Thead>
<Tr>
<Th>Name</Th>
<Th>Namespace</Th>
<Th>Labels</Th>
<Th>Annotations</Th>
<Th>Status</Th>
</Tr>
</Thead>
<Tbody>
{props.pods.map(pod => (
<Tr key={pod.metadata?.uid}>
<Td dataLabel='Name'>{pod.metadata?.name}</Td>
<Td dataLabel='Namespace'>{pod.metadata?.namespace}</Td>
<Td dataLabel='Labels'>
<DescriptionList>
{Object.entries(pod.metadata?.labels || {}).map(([key, value]) => {
return (
<DescriptionListGroup key={key}>
<DescriptionListTerm>{key}</DescriptionListTerm>
<DescriptionListDescription>{value as string}</DescriptionListDescription>
</DescriptionListGroup>
)
})}
</DescriptionList>
</Td>
<Td dataLabel='Annotations'>
<DescriptionList>
{Object.entries(pod.metadata?.annotations || {}).map(([key, value]) => {
return (
<DescriptionListGroup key={key}>
<DescriptionListTerm>{key}</DescriptionListTerm>
<DescriptionListDescription>{value as string}</DescriptionListDescription>
</DescriptionListGroup>
)
})}
</DescriptionList>
</Td>
<Td dataLabel='Status'>{k8Service.podStatus(pod)}</Td>
</Tr>
))}
</Tbody>
</Table>
)}
</PanelMainBody>
</PanelMain>
</Panel>
)
}
Loading

0 comments on commit 58ba7d6

Please sign in to comment.