Skip to content

Commit

Permalink
feat: add more info to pod details view (#837)
Browse files Browse the repository at this point in the history
Co-authored-by: michaeljguarino <[email protected]>
  • Loading branch information
floreks and michaeljguarino authored Apr 15, 2024
1 parent 82e6625 commit db81e1f
Show file tree
Hide file tree
Showing 35 changed files with 372 additions and 151 deletions.
15 changes: 14 additions & 1 deletion assets/schema/kubernetes-schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2412,7 +2412,7 @@ type pod_Pod {
type pod_ContainerStatus {
name: String!
ready: Boolean!
state: String!
state: ContainerState!
}

type pod_PodMetrics {
Expand Down Expand Up @@ -3787,6 +3787,7 @@ type pod_Container {
resources: v1_ResourceRequirements
securityContext: v1_SecurityContext!
startupProbe: v1_Probe!
state: ContainerState!
status: v1_ContainerStatus!
volumeMounts: [pod_VolumeMount]!
}
Expand Down Expand Up @@ -5737,6 +5738,18 @@ enum HTTPMethod {
PATCH
}

enum ContainerState {
Waiting
Running
Terminated
Failed
Unknown
}

type Map {
map: ObjMap!
}

type StateWrapper {
state: ContainerState!
}
1 change: 0 additions & 1 deletion assets/src/components/kubernetes/cluster/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,6 @@ export function NodePods(): ReactElement {
}}
queryName="handleGetNodePods"
itemsKey="pods"
disableOnRowClick
/>
)
}
Expand Down
3 changes: 3 additions & 0 deletions assets/src/components/kubernetes/common/Conditions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ export default function Conditions({
height: '100%',
...(maxHeight ? { maxHeight } : {}),
}}
emptyStateProps={{
message: 'No conditions found.',
}}
/>
)
}
220 changes: 180 additions & 40 deletions assets/src/components/kubernetes/common/Containers.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { ReactElement } from 'react'
import { Chip, ChipList, CloseIcon, Code } from '@pluralsh/design-system'
import yaml from 'js-yaml'

import {
Pod_Container as ContainerT,
Maybe,
V1_Probe as ProbeT,
} from '../../../generated/graphql-kubernetes'
import { ContainerStatus } from '../../cluster/ContainerStatuses'
import { toReadiness } from '../workloads/utils'
Expand Down Expand Up @@ -57,7 +59,7 @@ function Container({ container }: ContainerProps): ReactElement {
<ContainerStatus
status={{
name: '',
readiness: toReadiness(container.status),
readiness: toReadiness(container?.state),
}}
/>
<span>{container.name}</span>
Expand All @@ -76,7 +78,17 @@ function Container({ container }: ContainerProps): ReactElement {
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Commands">
{container.commands ? (
<Code>{container.commands.join('\n')}</Code>
// @ts-ignore
<Code
css={{
pre: {
whiteSpace: 'break-spaces',
wordBreak: 'break-word',
},
}}
>
{container.commands.join('\n')}
</Code>
) : (
'-'
)}
Expand All @@ -87,10 +99,9 @@ function Container({ container }: ContainerProps): ReactElement {
<ResourceInfoCardEntry heading="Restarts">
{container.status?.restartCount}
</ResourceInfoCardEntry>
{/* <Entry heading="Volume mounts">{container.volumeMounts}</Entry> */}
</ResourceInfoCardSection>

<ResourceInfoCardSection heading="Security context">
<ResourceInfoCardSection heading="Security">
<ResourceInfoCardEntry heading="Privileged">
{container.securityContext?.privileged}
</ResourceInfoCardEntry>
Expand All @@ -109,54 +120,183 @@ function Container({ container }: ContainerProps): ReactElement {
<ResourceInfoCardEntry heading="Read-only root filesystem">
{container.securityContext?.readOnlyRootFilesystem}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Proc mount">
{container.securityContext?.procMount}
</ResourceInfoCardEntry>
</ResourceInfoCardSection>

{/* {container.securityContext?.capabilities} */}
{/* {container.securityContext?.seLinuxOptions} */}
{/* {container.securityContext?.windowsOptions} */}
{/* {container.securityContext?.procMount} */}
{/* {container.securityContext?.seccompProfile} */}
<ResourceInfoCardSection heading="Seccomp profile">
<ResourceInfoCardEntry heading="Type">
{container.securityContext?.seccompProfile?.type}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Profile">
{container.securityContext?.seccompProfile?.localhostProfile}
</ResourceInfoCardEntry>
</ResourceInfoCardSection>

<ResourceInfoCardSection heading="Windows Options">
<ResourceInfoCardEntry heading="Run as user">
{container.securityContext?.windowsOptions?.runAsUserName}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Host process">
{container.securityContext?.windowsOptions?.hostProcess}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="GMSA credentials spec name">
{container.securityContext?.windowsOptions?.gmsaCredentialSpecName}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="GMSA credential spec">
{container.securityContext?.windowsOptions?.gmsaCredentialSpec}
</ResourceInfoCardEntry>
</ResourceInfoCardSection>

<ResourceInfoCardSection heading="SELinux options">
<ResourceInfoCardEntry heading="Role">
{container.securityContext?.seLinuxOptions?.role && (
<Chip size="small">
{container.securityContext?.seLinuxOptions?.role}
</Chip>
)}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Level">
{container.securityContext?.seLinuxOptions?.level && (
<Chip size="small">
{container.securityContext?.seLinuxOptions?.level}
</Chip>
)}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="User">
{container.securityContext?.seLinuxOptions?.user && (
<Chip size="small">
{container.securityContext?.seLinuxOptions?.user}
</Chip>
)}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Type">
{container.securityContext?.seLinuxOptions?.type && (
<Chip size="small">
{container.securityContext?.seLinuxOptions?.type}
</Chip>
)}
</ResourceInfoCardEntry>
</ResourceInfoCardSection>

<ResourceInfoCardSection heading="Capabilities">
<ResourceInfoCardEntry heading="Add">
{container.securityContext?.capabilities?.add && (
<ChipList
values={container.securityContext?.capabilities?.add ?? []}
limit={3}
emptyState={<>-</>}
/>
)}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Drop">
{container.securityContext?.capabilities?.drop && (
<ChipList
values={container.securityContext?.capabilities?.drop ?? []}
limit={3}
emptyState={<>-</>}
/>
)}
</ResourceInfoCardEntry>
</ResourceInfoCardSection>

<ResourceInfoCardSection heading="Resources">
<ResourceInfoCardEntry heading="Claims">
<ChipList
size="small"
values={Object.entries(
container?.resources?.claims?.map((c) => c?.name) ?? []
)}
limit={3}
emptyState={<CloseIcon />}
/>
{container?.resources?.claims && (
<ChipList
size="small"
values={Object.entries(
container?.resources?.claims?.map((c) => c?.name)
)}
limit={3}
emptyState={<CloseIcon />}
/>
)}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Requests">
<ChipList
size="small"
values={Object.entries(container?.resources?.requests ?? [])}
transformValue={(label) => label.join(': ')}
limit={3}
emptyState={<CloseIcon />}
/>
{container?.resources?.requests && (
<ChipList
size="small"
values={Object.entries(container?.resources?.requests ?? [])}
transformValue={(label) => label.join(': ')}
limit={3}
emptyState={<CloseIcon />}
/>
)}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Limits">
<ChipList
size="small"
values={Object.entries(container?.resources?.limits ?? [])}
transformValue={(label) => label.join(': ')}
limit={3}
emptyState={<CloseIcon />}
/>
{container?.resources?.limits && (
<ChipList
size="small"
values={Object.entries(container?.resources?.limits ?? [])}
transformValue={(label) => label.join(': ')}
limit={3}
emptyState={<CloseIcon />}
/>
)}
</ResourceInfoCardEntry>
{/* {container.securityContext?.capabilities} */}
{/* {container.securityContext?.seLinuxOptions} */}
{/* {container.securityContext?.windowsOptions} */}
{/* {container.securityContext?.procMount} */}
{/* {container.securityContext?.seccompProfile} */}
</ResourceInfoCardSection>

{/* <SidecarItem heading="Liveness">{container.livenessProbe}</SidecarItem> */}
{/* <SidecarItem heading="Readines">{container.readinessProbe}</SidecarItem> */}
{/* <SidecarItem heading="Startup">{container.startupProbe}</SidecarItem> */}
{/* <SidecarItem heading="Resources">{container.resources}</SidecarItem> */}
<Probe
heading="Liveness probe"
probe={container?.livenessProbe}
/>
<Probe
heading="Readiness probe"
probe={container?.readinessProbe}
/>
<Probe
heading="Startup probe"
probe={container?.startupProbe}
/>
</ResourceInfoCard>
)
}

function Probe({
heading,
probe,
}: {
heading: string
probe: ProbeT
}): ReactElement {
return (
<ResourceInfoCardSection heading={heading}>
<ResourceInfoCardEntry heading="Failure threshold">
{probe?.failureThreshold}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Success threshold">
{probe?.successThreshold}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Termination grace period seconds">
{probe?.terminationGracePeriodSeconds}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Timeout seconds">
{probe?.timeoutSeconds}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Period seconds">
{probe?.periodSeconds}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Initial delay seconds">
{probe?.initialDelaySeconds}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Exec">
{probe?.exec?.command}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Grpc">
{probe?.grpc?.service &&
probe?.grpc?.port &&
`${probe?.grpc?.service}:${probe?.grpc?.port}`}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="HTTP Get">
{probe?.httpGet && <Code>{yaml.dump(probe?.httpGet)}</Code>}
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="TCP socket">
{probe?.tcpSocket?.host &&
probe?.tcpSocket?.port &&
`${probe?.tcpSocket?.host}:${probe?.tcpSocket?.port}`}
</ResourceInfoCardEntry>
</ResourceInfoCardSection>
)
}
58 changes: 58 additions & 0 deletions assets/src/components/kubernetes/common/ImagePullSecrets.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { createColumnHelper } from '@tanstack/react-table'
import { ReactElement } from 'react'
import { Table } from '@pluralsh/design-system'
import { Link } from 'react-router-dom'

import { getResourceDetailsAbsPath } from '../../../routes/kubernetesRoutesConsts'
import { InlineLink } from '../../utils/typography/InlineLink'

const columnHelper = createColumnHelper<SecretReference>()

interface SecretReference {
clusterId: string
name: string
namespace: string
}

interface ImagePullSecretsProps {
imagePullSecrets: Nullable<Array<SecretReference>>
maxHeight?: string
}

const columns = [
// Name
columnHelper.accessor((ref) => ref, {
id: 'name',
header: 'Name',
cell: ({ getValue }) => {
const { clusterId, name, namespace } = getValue()

return (
<Link
to={getResourceDetailsAbsPath(clusterId, 'secret', name, namespace)}
>
<InlineLink>{name}</InlineLink>
</Link>
)
},
}),
]

export default function ImagePullSecrets({
imagePullSecrets,
maxHeight,
}: ImagePullSecretsProps): ReactElement {
return (
<Table
data={imagePullSecrets ?? []}
columns={columns}
css={{
height: '100%',
...(maxHeight ? { maxHeight } : {}),
}}
emptyStateProps={{
message: 'No Image Pull Secrets found.',
}}
/>
)
}
2 changes: 0 additions & 2 deletions assets/src/components/kubernetes/common/ResourceDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ import { useTheme } from 'styled-components'
import { useMatch, useResolvedPath } from 'react-router-dom'

import { ResponsiveLayoutPage } from '../../utils/layout/ResponsiveLayoutPage'

import { LinkTabWrap } from '../../utils/Tabs'
import { ResponsivePageFullWidth } from '../../utils/layout/ResponsivePageFullWidth'
import { ResponsiveLayoutSpacer } from '../../utils/layout/ResponsiveLayoutSpacer'
import { ResponsiveLayoutSidecarContainer } from '../../utils/layout/ResponsiveLayoutSidecarContainer'

import { ResponsiveLayoutHeader } from '../../utils/layout/ResponsiveLayoutHeader'
import { PageHeaderContext } from '../../cd/ContinuousDeployment'

Expand Down
Loading

0 comments on commit db81e1f

Please sign in to comment.