diff --git a/client/package-lock.json b/client/package-lock.json index 921549a..907732e 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1914,6 +1914,11 @@ "resolved": "https://nexus.corp.indeed.com/repository/npm/@types/stack-utils/-/stack-utils-1.0.1.tgz", "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==" }, + "@types/yamljs": { + "version": "0.2.31", + "resolved": "https://nexus.corp.indeed.com/repository/npm/@types/yamljs/-/yamljs-0.2.31.tgz", + "integrity": "sha512-QcJ5ZczaXAqbVD3o8mw/mEBhRvO5UAdTtbvgwL/OgoWubvNBh6/MxLBAigtcgIFaq3shon9m3POIxQaLQt4fxQ==" + }, "@types/yargs": { "version": "13.0.9", "resolved": "https://nexus.corp.indeed.com/repository/npm/@types/yargs/-/yargs-13.0.9.tgz", diff --git a/client/package.json b/client/package.json index de24361..ff04d7b 100644 --- a/client/package.json +++ b/client/package.json @@ -12,6 +12,7 @@ "@types/react-dom": "^16.9.8", "@types/react-modal": "1.6.8", "@types/react-select": "^3.0.16", + "@types/yamljs": "^0.2.31", "ansi-to-react": "^5.1.1", "humanize-duration": "^3.23.1", "js-base64": "^2.6.4", diff --git a/client/src/components/error.tsx b/client/src/components/error.tsx index bd7f459..31e945a 100644 --- a/client/src/components/error.tsx +++ b/client/src/components/error.tsx @@ -1,7 +1,7 @@ import './error.scss'; import React from 'react'; -const Error = ({messages}: {messages: [string]}) => ( +const Error = ({messages}: {messages: string[]}) => (
{messages.map((x, i) => (
{x}
diff --git a/client/src/components/eventsPanel.tsx b/client/src/components/eventsPanel.tsx index 7c3ae86..cf49a50 100644 --- a/client/src/components/eventsPanel.tsx +++ b/client/src/components/eventsPanel.tsx @@ -4,28 +4,13 @@ import {TableBody} from './listViewHelpers'; import log from '../utils/log'; import ResourceSvg from '../art/resourceSvg'; import Sorter, {sortByDate, SortInfo} from './sorter'; +import { K8sEvent, InvolvedObject } from '../utils/types'; -type Metadata = { - name: string; - creationTimestamp: number; -} -type InvolvedObject = { - kind: string; - namespace: string; - name: string; -} -type Event = { - metadata: Metadata; - involvedObject: InvolvedObject; - message: string; - reason: string; - type: string; -} type EventsPanelProps = { - items: Event[]; - filter: React.ReactNode; + items?: K8sEvent[]; + filter?: React.ReactNode; shortList?: boolean; - sort: SortInfo; + sort?: SortInfo; } export default function EventsPanel({items, filter, shortList, sort} : EventsPanelProps) { @@ -59,17 +44,17 @@ export default function EventsPanel({items, filter, shortList, sort} : EventsPan filter={filter} colSpan={shortList ? 4 : 5} sort={sort} - row={(event: Event) => ( + row={(event) => ( -
{event.involvedObject.kind}
+
{event.involvedObject!.kind}
{!shortList && ( - {getName(event.involvedObject)} + {getName(event.involvedObject!)} )} {fromNow(event.metadata.creationTimestamp)} {event.reason} @@ -85,11 +70,11 @@ export default function EventsPanel({items, filter, shortList, sort} : EventsPan function getName({kind, namespace, name} : InvolvedObject) { const text = namespace ? `${namespace}:${name}` : name; - const href = getHref({kind, namespace, name}); + const href = getHref(kind, namespace, name); return href ? ({text}) : text; } -function getHref({kind, namespace, name} : InvolvedObject) { +function getHref(kind: string, namespace: string, name: string) { switch (kind) { case 'ClusterRole': return `/#!clusterrole/${name}`; case 'ClusterRoleBinding': return `/#!clusterrolebinding/${name}`; @@ -113,8 +98,8 @@ function getHref({kind, namespace, name} : InvolvedObject) { } } -function sortByName(event: Event) { - return `${event.involvedObject.namespace}:${event.involvedObject.name}`; +function sortByName(event: K8sEvent) { + return `${event.involvedObject!.namespace}:${event.involvedObject!.name}`; } function getTypeClass(type: string) { diff --git a/client/src/components/filter.tsx b/client/src/components/filter.tsx index 32989ac..a03984c 100644 --- a/client/src/components/filter.tsx +++ b/client/src/components/filter.tsx @@ -6,7 +6,7 @@ type FilterProps = { text: string; filter: string; onChange: (value: string) => void; - onNamespaceChange?: Function; + onNamespaceChange?: (namespace: string) => void; } const Filter = ({text, filter, onChange, onNamespaceChange}: FilterProps) => ( diff --git a/client/src/components/itemHeader.tsx b/client/src/components/itemHeader.tsx index 5537e44..f2e7579 100644 --- a/client/src/components/itemHeader.tsx +++ b/client/src/components/itemHeader.tsx @@ -3,7 +3,7 @@ import Base from './base'; interface ItemHeaderProps { title: string[]; - ready: boolean; + ready?: boolean; } export default class ItemHeader extends Base { diff --git a/client/src/components/listViewHelpers.tsx b/client/src/components/listViewHelpers.tsx index 932caad..7784d6d 100644 --- a/client/src/components/listViewHelpers.tsx +++ b/client/src/components/listViewHelpers.tsx @@ -7,6 +7,14 @@ import Sorter, {sortByDate, SortInfo} from './sorter'; import ResourceSvg from '../art/resourceSvg'; import {TODO} from '../utils/types'; +type TableBodyProps = { + items?: T[] | null; + filter: TODO; + colSpan: number; + sort?: SortInfo; + row: (item: T) => ReactNode; +} + export function objectMap(items: {[key: string]: string} = {}) { return Object.entries(items).map(([key, value]) => { const substrValue = value.length <= 50 ? value : `${value.substr(0, 50)}...`; @@ -30,14 +38,6 @@ export function objectMap(items: {[key: string]: string} = {}) { }); } -type TableBodyProps = { - items?: T[] | null; - filter: TODO; - colSpan: number; - sort: SortInfo; - row: (item: T) => ReactNode; -} - export function TableBody({items, filter, colSpan, sort, row}: TableBodyProps) { if (items && sort) { const {field, direction} = sort; diff --git a/client/src/components/nodeCpuChart.tsx b/client/src/components/nodeCpuChart.tsx index 0b175c2..f103961 100644 --- a/client/src/components/nodeCpuChart.tsx +++ b/client/src/components/nodeCpuChart.tsx @@ -3,9 +3,9 @@ import React from 'react'; import Chart from './chart'; import LoadingChart from './loadingChart'; import {parseCpu, TO_ONE_CPU} from '../utils/unitHelpers'; -import {TODO} from '../utils/types'; +import {Node, Metrics} from '../utils/types'; -export default function NodeCpuChart({items, metrics}: {items: TODO[], metrics: TODO[]}) { +export default function NodeCpuChart({items, metrics}: {items?: Node[], metrics?: _.Dictionary}) { const totals = getNodeCpuTotals(items, metrics); return ( @@ -21,7 +21,7 @@ export default function NodeCpuChart({items, metrics}: {items: TODO[], metrics: ); } -function getNodeCpuTotals(items: TODO[], metrics: TODO[]) { +function getNodeCpuTotals(items?: Node[], metrics?: _.Dictionary) { if (!items || !metrics) return null; const metricValues = Object.values(metrics); diff --git a/client/src/components/nodeRamChart.tsx b/client/src/components/nodeRamChart.tsx index 2c4d2c2..9f84607 100644 --- a/client/src/components/nodeRamChart.tsx +++ b/client/src/components/nodeRamChart.tsx @@ -3,9 +3,9 @@ import React from 'react'; import Chart from './chart'; import LoadingChart from './loadingChart'; import {parseRam, TO_GB} from '../utils/unitHelpers'; -import {TODO} from '../utils/types'; +import {Node, Metrics} from '../utils/types'; -export default function NodeRamChart({items, metrics}: {items: TODO[], metrics: TODO[]}) { +export default function NodeRamChart({items, metrics}: {items?: Node[], metrics?: _.Dictionary}) { const totals = getNodeRamTotals(items, metrics); return (
@@ -20,7 +20,7 @@ export default function NodeRamChart({items, metrics}: {items: TODO[], metrics: ); } -function getNodeRamTotals(items: TODO[], metrics: TODO[]) { +function getNodeRamTotals(items?: Node[], metrics?: _.Dictionary) { if (!items || !metrics) return null; const metricValues = Object.values(metrics); diff --git a/client/src/components/saveButton.tsx b/client/src/components/saveButton.tsx index bc719de..e769eec 100644 --- a/client/src/components/saveButton.tsx +++ b/client/src/components/saveButton.tsx @@ -3,17 +3,18 @@ import Base from './base'; import Button from './button'; import EditorModal from '../views/editorModal'; import EditSvg from '../art/editSvg'; +import { ApiItem } from '../utils/types'; -interface SaveButtonProps { - onSave: Function - item: {} +interface SaveButtonProps> { + item: T; + onSave: (item: T) => Promise; } interface SaveButtonStates { showEditor: boolean; } -export default class SaveButton extends Base { +export default class SaveButton> extends Base, SaveButtonStates> { render() { const {onSave, item} = this.props; const {showEditor = null} = this.state || {}; diff --git a/client/src/components/sorter.tsx b/client/src/components/sorter.tsx index b4faf70..c12e27d 100644 --- a/client/src/components/sorter.tsx +++ b/client/src/components/sorter.tsx @@ -15,7 +15,7 @@ export interface SortInfo { interface SorterProps { field: TODO; - sort: { + sort?: { field: string, direction: Direction, onSort: Function, @@ -28,7 +28,7 @@ interface SorterStates { type SortCallback = (item: TODO) => void; -export function defaultSortInfo(target: Component | SortCallback, field = 'metadata.name', direction: Direction = 'asc'): SortInfo { +export function defaultSortInfo(target: Component | SortCallback, field: TODO = 'metadata.name', direction: Direction = 'asc'): SortInfo { return { field, direction, @@ -79,11 +79,11 @@ export default class Sorter extends Base { isUpSelected() { const {sort} = this.props; - return (this.isSelected() && sort.direction === 'asc'); + return (this.isSelected() && sort!.direction === 'asc'); } isDownSelected() { const {sort} = this.props; - return (this.isSelected() && sort.direction === 'desc'); + return (this.isSelected() && sort!.direction === 'desc'); } } diff --git a/client/src/services/api.ts b/client/src/services/api.ts index 9f0cfa3..d324b0e 100644 --- a/client/src/services/api.ts +++ b/client/src/services/api.ts @@ -102,11 +102,11 @@ function metricsFactory() { return { nodes: (cb: DataCallback) => metrics('/apis/metrics.k8s.io/v1beta1/nodes', cb), node: (name: string, cb: DataCallback) => metrics(`/apis/metrics.k8s.io/v1beta1/nodes/${name}`, cb), - pods: (namespace: string, cb: DataCallback) => metrics(url(namespace), cb), + pods: (namespace: string | undefined, cb: DataCallback) => metrics(url(namespace), cb), pod: (namespace: string, name: string, cb: DataCallback) => metrics(`/apis/metrics.k8s.io/v1beta1/namespaces/${namespace}/pods/${name}`, cb), }; - function url(namespace: string) { + function url(namespace?: string) { return namespace ? `/apis/metrics.k8s.io/v1beta1/namespaces/${namespace}/pods` : '/apis/metrics.k8s.io/v1beta1/pods'; } } diff --git a/client/src/services/apiProxy.ts b/client/src/services/apiProxy.ts index 40f2a54..5e1d1ab 100644 --- a/client/src/services/apiProxy.ts +++ b/client/src/services/apiProxy.ts @@ -74,7 +74,7 @@ export function apiFactoryWithNamespace>(group: stri const apiRoot = getApiRoot(group, version); return { resource: {group, resource}, - list: (namespace: string, cb: StreamCallback, errCb?: ErrorCallback) => streamResults(url(namespace), cb, errCb), + list: (namespace: string | undefined, cb: StreamCallback, errCb?: ErrorCallback) => streamResults(url(namespace), cb, errCb), get: (namespace: string, name: string, cb: StreamCallback, errCb?: ErrorCallback) => streamResult(url(namespace), name, cb, errCb), post: (body: any) => post(url(body.metadata.namespace), body), put: (body: any) => put(`${url(body.metadata.namespace)}/${body.metadata.name}`, body), @@ -82,7 +82,7 @@ export function apiFactoryWithNamespace>(group: stri scale: includeScale ? apiScaleFactory(apiRoot, resource) : undefined, }; - function url(namespace: string) { + function url(namespace?: string) { return namespace ? `${apiRoot}/namespaces/${namespace}/${resource}` : `${apiRoot}/${resource}`; } } diff --git a/client/src/services/docs.js b/client/src/services/docs.ts similarity index 64% rename from client/src/services/docs.js rename to client/src/services/docs.ts index a6692cf..952917c 100644 --- a/client/src/services/docs.js +++ b/client/src/services/docs.ts @@ -1,14 +1,15 @@ import Swagger from 'swagger-parser'; import apis from './api'; +import { TODO } from '../utils/types'; -let docsPromise; +let docsPromise: Promise; async function getDocs() { const docs = await apis.swagger(); return Swagger.dereference(docs); } -export default async function getDocDefinitions(apiVersion, kind) { +export default async function getDocDefinitions(apiVersion: string, kind: string) { if (!docsPromise) { docsPromise = getDocs(); // Don't wait here. Just kick off the request } @@ -22,10 +23,10 @@ export default async function getDocDefinitions(apiVersion, kind) { } return Object.values(definitions) - .filter(x => !!x['x-kubernetes-group-version-kind']) - .find(x => x['x-kubernetes-group-version-kind'].some(comparer)); + .filter((x: TODO) => !!x['x-kubernetes-group-version-kind']) + .find((x: TODO) => x['x-kubernetes-group-version-kind'].some(comparer)); - function comparer(info) { + function comparer(info: TODO) { return info.group === group && info.version === version && info.kind === kind; diff --git a/client/src/utils/filterHelper.ts b/client/src/utils/filterHelper.ts index 0c7604f..4b4eb10 100644 --- a/client/src/utils/filterHelper.ts +++ b/client/src/utils/filterHelper.ts @@ -1,4 +1,4 @@ -import {TODO} from './types'; +import {ApiItem} from './types'; export default function test(filter = '', ...values: string[]) { const value = filter.toLowerCase(); @@ -7,8 +7,8 @@ export default function test(filter = '', ...values: string[]) { .some(x => x.toLowerCase().includes(value)); } -export function filterByOwner(items: TODO[], owner: TODO) { - if (!items || !owner) return null; +export function filterByOwner>(items?: T[], owner?: ApiItem): T[] | undefined { + if (!items || !owner) return undefined; const {uid} = owner.metadata; @@ -20,15 +20,15 @@ export function filterByOwner(items: TODO[], owner: TODO) { }); } -export function filterByOwners(items: TODO[], owners: TODO) { - if (!items || !owners) return null; +export function filterByOwners>(items?: T[], owners?: ApiItem[]): T[] | undefined { + if (!items || !owners) return undefined; - const uids = owners.map((x: any) => x.metadata.uid); + const uidList = owners.map(x => x.metadata.uid); return items.filter((x) => { - if (x.involvedObject && uids.includes(x.involvedObject.uid)) return true; + if (x.involvedObject && uidList.includes(x.involvedObject.uid)) return true; const {ownerReferences} = x.metadata; - return ownerReferences && ownerReferences.some((y: {uid: string}) => uids.includes(y.uid)); + return ownerReferences && ownerReferences.some((y: {uid: string}) => uidList.includes(y.uid)); }); } diff --git a/client/src/utils/metricsHelpers.tsx b/client/src/utils/metricsHelpers.tsx index 6787d39..86e0320 100644 --- a/client/src/utils/metricsHelpers.tsx +++ b/client/src/utils/metricsHelpers.tsx @@ -1,8 +1,10 @@ import _ from 'lodash'; import {parseRam, parseCpu} from './unitHelpers'; -import {TODO, Pod, Node, Metrics} from './types'; +import {TODO, ApiItem, Pod, Node, Metrics, MetricsUsage} from './types'; -export default function getMetrics(items?: Pod[], metrics?: Metrics[]) { +type ResourceType = 'cpu' | 'memory' + +export default function getMetrics(items?: ApiItem[], metrics?: Metrics[]) { if (!items || !metrics) return undefined; const names = _.map(items, x => x.metadata.name); @@ -13,7 +15,7 @@ export default function getMetrics(items?: Pod[], metrics?: Metrics[]) { // Node helpers -export function getNodeResourceValue(node: Node | undefined, pods: Pod[] | undefined, resource: string, type: string, phases: string[]) { +export function getNodeResourceValue(node: Node | undefined, pods: Pod[] | undefined, resource: ResourceType, type: string, phases: string[]) { if (!node || !pods) return null; return _(pods) @@ -22,23 +24,23 @@ export function getNodeResourceValue(node: Node | undefined, pods: Pod[] | undef .sumBy(x => getPodResourceValue(x, resource, type)); } -export function getNodeResourcePercent(node: TODO, pods: TODO, resource: string, type: string) { +export function getNodeResourcePercent(node: Node, pods: Pod[], resource: ResourceType, type: string) { const used = getNodeResourceValue(node, pods, resource, type, ['Running']); const available = getNodeResourcesAvailable(node, resource); return used && available ? used / available : null; } -export function getNodeUsagePercent(node: TODO, metrics: TODO, resource: string) { +export function getNodeUsagePercent(node: Node, metrics: _.Dictionary, resource: ResourceType) { const used = getNodeUsage(node, metrics, resource); const available = getNodeResourcesAvailable(node, resource); return used && available ? used / available : null; } -export function getNodeResourcesAvailable(node: TODO, resource: string) { +export function getNodeResourcesAvailable(node: Node, resource: ResourceType) { return node ? parse(resource, node.status.capacity) : null; } -export function getNodeUsage(node: TODO, metrics: TODO, resource: string) { +export function getNodeUsage(node: Node, metrics: _.Dictionary, resource: ResourceType) { if (!node || !metrics) return null; const result = metrics[node.metadata.name]; @@ -47,26 +49,27 @@ export function getNodeUsage(node: TODO, metrics: TODO, resource: string) { // Pod helpers -export function getPodResourcePercent(item: TODO, metrics: TODO, resource: string, type: string) { - const actual = getPodUsage(item, metrics, resource); - const request = getPodResourceValue(item, resource, type); +export function getPodResourcePercent(pod: Pod, metrics: _.Dictionary, resource: ResourceType, type: string) { + const actual = getPodUsage(pod, metrics, resource); + const request = getPodResourceValue(pod, resource, type); return actual ? actual / request : null; } -export function getPodUsage(pod: TODO, metrics: TODO, resource: string) { +export function getPodUsage(pod: Pod, metrics: _.Dictionary, resource: ResourceType) { if (!pod || !metrics) return null; const metric = metrics[pod.metadata.name] || {}; - return _.sumBy(metric.containers, (x: TODO) => parse(resource, x.usage)); + return _.sumBy(metric.containers, x => parse(resource, x.usage)); } -export function getPodResourceValue(pod: TODO, resource: string, type: string) { +export function getPodResourceValue(pod: TODO, resource: ResourceType, type: string) { return _(pod.spec.containers) .filter(x => x.resources && x.resources[type]) .sumBy(x => parse(resource, x.resources[type])); } -function parse(resource: string, target: {[key: string]: string}) { +function parse(resource: ResourceType, target: MetricsUsage) { const parser = resource === 'cpu' ? parseCpu : parseRam; + // @ts-ignore return parser(target[resource]); } diff --git a/client/src/utils/nodeHelpers.ts b/client/src/utils/nodeHelpers.ts index 3f76b91..ead7d46 100644 --- a/client/src/utils/nodeHelpers.ts +++ b/client/src/utils/nodeHelpers.ts @@ -1,12 +1,12 @@ -import {TODO} from './types'; +import {Node} from './types'; /** * @param {*status* object with a status field (most likely the row from the TableBody)} * @returns the status text, as defined in https://kubernetes.io/docs/concepts/architecture/nodes/#condition */ -function getReadyStatus({status}: TODO) { - if (!status.conditions) return null; - const ready = status.conditions.find((y: {type: string}) => y.type === 'Ready'); +function getReadyStatus({status}: Node) { + if (!status.conditions) return undefined; + const ready = status.conditions.find(y => y.type === 'Ready'); return ready && ready.status; } diff --git a/client/src/utils/types.ts b/client/src/utils/types.ts index 2175963..3dad45f 100644 --- a/client/src/utils/types.ts +++ b/client/src/utils/types.ts @@ -2,15 +2,31 @@ export type TODO = any; export type ApiItem = { kind: string; + apiVersion: string; metadata: Metadata; spec: TSpec; - status: TStatus + status: TStatus; + involvedObject?: InvolvedObject; +} + +export interface InvolvedObject { + uid: string; + kind: string; + namespace: string; + name: string; } interface Metadata { uid: string; resourceVersion: string; + creationTimestamp: number; name: string; + labels: {[name: string]: string}; + ownerReferences?: { + uid: string; + kind: string; + name: string; + }[]; } interface Container { @@ -26,8 +42,28 @@ interface Container { } } +interface Condition { + type: string; + status: string; + lastTransitionTime: number; + reason: string; + message: string; +} + +export interface MetricsUsage { + cpu?: string; + memory?: string; +} + export interface Metrics extends ApiItem { containers: Container[]; + resources?: { + requests?: { + cpu?: string; + memory?: string; + } + } + usage: MetricsUsage } interface NamespaceStatus { @@ -37,20 +73,50 @@ interface NamespaceStatus { export interface Namespace extends ApiItem { } -export interface Node extends ApiItem { +interface NodeSpec { + taints: {[name: string]: string} +} + +export interface NodeStatus { + capacity: { + cpu?: string; + memory?: string; + } + conditions: Condition[]; + nodeInfo: { + kernelVersion: string; + osImage: string; + operatingSystem: string; + architecture: string; + containerRuntimeVersion: string; + kubeletVersion: string; + kubeProxyVersion: string; + } +} + +export interface Node extends ApiItem { } -export interface K8sEvent extends ApiItem{ +export interface K8sEvent extends ApiItem { + type: string; + reason: string; + message: string; } interface PodSpec { nodeName: string; containers: Container[]; + nodeSelector?: {[key: string]: string}; } interface PodStatus { phase: string; + hostIP: string; + podIP: string; + qosClass: string; + message: string; + conditions?: Condition[]; } export interface Pod extends ApiItem{ diff --git a/client/src/views/editorModal.js b/client/src/views/editorModal.tsx similarity index 88% rename from client/src/views/editorModal.js rename to client/src/views/editorModal.tsx index 8aaf02b..1c38ac6 100644 --- a/client/src/views/editorModal.js +++ b/client/src/views/editorModal.tsx @@ -8,9 +8,22 @@ import Doc from '../components/doc'; import getDocDefinitions from '../services/docs'; import LightBulbSvg from '../art/lightBulbSvg'; import EditSvg from '../art/editSvg'; +import { ApiItem, TODO } from '../utils/types'; -export default class EditorModal extends Base { - state = { +interface Props> { + body: T; + onSave: (item: T) => Promise; + onRequestClose: () => void; +} + +type State = { + showDocs: boolean; + yaml?: string; + properties?: TODO; +} + +export default class EditorModal> extends Base, State> { + state: State = { showDocs: false, }; @@ -18,7 +31,7 @@ export default class EditorModal extends Base { this.findDocs(this.props.body); } - async onEdit(yaml) { + async onEdit(yaml: string) { this.setState({yaml}); try { @@ -39,10 +52,10 @@ export default class EditorModal extends Base { if (shouldClose) this.close(); } - async findDocs(body) { + async findDocs(body: T) { if (!body || !body.apiVersion || !body.kind) return; - const result = await getDocDefinitions(body.apiVersion, body.kind); + const result: TODO = await getDocDefinitions(body.apiVersion, body.kind); if (!result) return; this.setState({properties: result.properties}); diff --git a/client/src/views/exec.js b/client/src/views/exec.js index f2eec26..b159405 100644 --- a/client/src/views/exec.js +++ b/client/src/views/exec.js @@ -30,7 +30,7 @@ export default class Exec extends Base { const {namespace, name} = this.props; const exec = api.exec(namespace, name, container, items => this.onData(items)); - this.socket = exec.getSocket(); // TODO: this won't work if the socket failes + this.socket = exec.getSocket(); // TODO: this won't work if the socket fails this.registerApi({cancel: exec.cancel}); if (this.xterm) this.xterm.reset(); diff --git a/client/src/views/node.js b/client/src/views/node.tsx similarity index 87% rename from client/src/views/node.js rename to client/src/views/node.tsx index dcde9d0..1370448 100644 --- a/client/src/views/node.js +++ b/client/src/views/node.tsx @@ -8,7 +8,7 @@ import Loading from '../components/loading'; import LoadingChart from '../components/loadingChart'; import MetadataFields from '../components/metadataFields'; import PodsPanel from '../components/podsPanel'; -import {defaultSortInfo} from '../components/sorter'; +import {defaultSortInfo, SortInfo} from '../components/sorter'; import Field from '../components/field'; import ChartsContainer from '../components/chartsContainer'; import NodeCpuChart from '../components/nodeCpuChart'; @@ -17,9 +17,22 @@ import PodStatusChart from '../components/podStatusChart'; import PodCpuChart from '../components/podCpuChart'; import PodRamChart from '../components/podRamChart'; import getMetrics from '../utils/metricsHelpers'; +import {Node, Pod, Metrics, TODO} from '../utils/types'; -export default class Node extends Base { - state = { +type Props = { + name: string; +} + +type State = { + podsSort: SortInfo; + item?: Node; + metrics?: Metrics[]; + pods?: Pod[]; + podMetrics?: Metrics[]; +} + +export default class NodeView extends Base { + state: State = { podsSort: defaultSortInfo(x => this.setState({podsSort: x})), }; @@ -29,8 +42,8 @@ export default class Node extends Base { this.registerApi({ item: api.node.get(name, item => this.setState({item})), metrics: api.metrics.node(name, metrics => this.setState({metrics})), - pods: api.pod.list(null, pods => this.setState({pods})), - podMetrics: api.metrics.pods(null, podMetrics => this.setState({podMetrics})), + pods: api.pod.list(undefined, pods => this.setState({pods})), + podMetrics: api.metrics.pods(undefined, podMetrics => this.setState({podMetrics})), }); } @@ -54,7 +67,13 @@ export default class Node extends Base { )}
Uptime
+ + {/* + // @ts-ignore */} + + {/* + // @ts-ignore */} @@ -119,18 +138,14 @@ export default class Node extends Base { } } -/** - * Render "taints" divs from the node spec - * - * @param spec spec from a node item - */ -function getTaints({spec}) { - return _.map(spec.taints, ({key, effect}) => ( + +function getTaints({spec}: Node) { + return _.map(spec.taints, ({key, effect}: TODO) => (
{key} {effect}
)); } -function getUptime({status}) { +function getUptime({status}: Node) { const ready = status.conditions.find(y => y.type === 'Ready'); if (!ready) return 'N/A'; diff --git a/client/src/views/nodes.js b/client/src/views/nodes.tsx similarity index 81% rename from client/src/views/nodes.js rename to client/src/views/nodes.tsx index a7413df..64947a2 100644 --- a/client/src/views/nodes.js +++ b/client/src/views/nodes.tsx @@ -2,7 +2,7 @@ import React from 'react'; import Base from '../components/base'; import ChartsContainer from '../components/chartsContainer'; import Filter from '../components/filter'; -import {defaultSortInfo} from '../components/sorter'; +import {defaultSortInfo, SortInfo} from '../components/sorter'; import NodeStatusChart from '../components/nodeStatusChart'; import api from '../services/api'; import test from '../utils/filterHelper'; @@ -11,9 +11,18 @@ import getReadyStatus from '../utils/nodeHelpers'; import NodeCpuChart from '../components/nodeCpuChart'; import NodeRamChart from '../components/nodeRamChart'; import getMetrics from '../utils/metricsHelpers'; +import { Node, Metrics, Pod } from '../utils/types'; -export default class Nodes extends Base { - state = { +type State = { + filter: string; + sort: SortInfo; + items?: Node[]; + metrics?: Metrics[]; + pods?: Pod[]; +}; + +export default class Nodes extends Base<{}, State> { + state: State = { filter: '', sort: defaultSortInfo(this, getReadyStatus, 'asc'), }; @@ -22,7 +31,7 @@ export default class Nodes extends Base { this.registerApi({ items: api.node.list(items => this.setState({items})), metrics: api.metrics.nodes(metrics => this.setState({metrics})), - pods: api.pod.list(null, pods => this.setState({pods})), + pods: api.pod.list(undefined, pods => this.setState({pods})), }); } @@ -57,8 +66,8 @@ export default class Nodes extends Base { } } -function filterNodes(items, filter) { - if (!items) return null; +function filterNodes(items?: Node[], filter?: string) { + if (!items) return undefined; return items.filter((x) => { const labels = x.metadata.labels || {}; diff --git a/client/src/views/pod.js b/client/src/views/pod.tsx similarity index 93% rename from client/src/views/pod.js rename to client/src/views/pod.tsx index 7f64076..c6b802b 100644 --- a/client/src/views/pod.js +++ b/client/src/views/pod.tsx @@ -19,10 +19,22 @@ import getMetrics from '../utils/metricsHelpers'; import PodCpuChart from '../components/podCpuChart'; import PodRamChart from '../components/podRamChart'; import ChartsContainer from '../components/chartsContainer'; +import { Pod, Metrics, K8sEvent } from '../utils/types'; + +type Props = { + namespace: string; + name: string; +} + +type State = { + item?: Pod; + metrics?: Metrics[]; + events?: K8sEvent[]; +} const service = api.pod; -export default class Pod extends Base { +export default class PodView extends Base { componentDidMount() { const {namespace, name} = this.props; @@ -39,6 +51,7 @@ export default class Pod extends Base { const errors = getErrors(item); const filteredEvents = filterByOwner(events, item); + // @ts-ignore const filteredMetrics = getMetrics(item && [item], metrics && [metrics]); return ( @@ -56,7 +69,7 @@ export default class Pod extends Base { service.put(x)} /> @@ -125,10 +138,10 @@ export default class Pod extends Base { } } -function getErrors(item) { +function getErrors(item?: Pod) { if (!item) return []; if (item.status.message) return [item.status.message]; if (item.status.conditions) return item.status.conditions.map(x => x.message).filter(x => !!x); - return null; + return undefined; } diff --git a/client/src/views/pods.js b/client/src/views/pods.tsx similarity index 83% rename from client/src/views/pods.js rename to client/src/views/pods.tsx index e2a848c..a6ed3dd 100644 --- a/client/src/views/pods.js +++ b/client/src/views/pods.tsx @@ -7,20 +7,29 @@ import PodCpuChart from '../components/podCpuChart'; import PodRamChart from '../components/podRamChart'; import PodsPanel from '../components/podsPanel'; import PodStatusChart from '../components/podStatusChart'; -import {defaultSortInfo} from '../components/sorter'; +import {defaultSortInfo, SortInfo} from '../components/sorter'; import getMetrics from '../utils/metricsHelpers'; import ChartsContainer from '../components/chartsContainer'; +import {Pod, Metrics} from '../utils/types'; -export default class Pods extends Base { - state = { +type State = { + namespace: string; + filter: string; + sort: SortInfo; + items?: Pod[]; + metrics?: Metrics[]; +} + +export default class Pods extends Base<{}, State> { + state: State = { namespace: '', filter: '', sort: defaultSortInfo(this), }; - setNamespace(namespace) { + setNamespace(namespace: string) { this.setState({namespace}); - this.setState({items: null}); + this.setState({items: undefined}); this.registerApi({ items: api.pod.list(namespace, items => this.setState({items})),