mirror of
https://github.com/skooner-k8s/skooner.git
synced 2026-08-24 04:16:18 +00:00
Converting Nodes, Node, Pods, and Pod to TS
This commit is contained in:
Generated
+5
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import './error.scss';
|
||||
import React from 'react';
|
||||
|
||||
const Error = ({messages}: {messages: [string]}) => (
|
||||
const Error = ({messages}: {messages: string[]}) => (
|
||||
<div className='error contentPanel'>
|
||||
{messages.map((x, i) => (
|
||||
<div key={i}>{x}</div>
|
||||
|
||||
@@ -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) => (
|
||||
<tr key={event.metadata.name}>
|
||||
<td className='td_icon optional_medium'>
|
||||
<ResourceSvg
|
||||
resource={event.involvedObject.kind}
|
||||
resource={event.involvedObject!.kind}
|
||||
className={getTypeClass(event.type)}
|
||||
/>
|
||||
<div className='td_iconLabel'>{event.involvedObject.kind}</div>
|
||||
<div className='td_iconLabel'>{event.involvedObject!.kind}</div>
|
||||
</td>
|
||||
{!shortList && (
|
||||
<td className='wrapped_name'>{getName(event.involvedObject)}</td>
|
||||
<td className='wrapped_name'>{getName(event.involvedObject!)}</td>
|
||||
)}
|
||||
<td>{fromNow(event.metadata.creationTimestamp)}</td>
|
||||
<td className='optional_small'>{event.reason}</td>
|
||||
@@ -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 ? (<a href={href}>{text}</a>) : 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) {
|
||||
|
||||
@@ -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) => (
|
||||
|
||||
@@ -3,7 +3,7 @@ import Base from './base';
|
||||
|
||||
interface ItemHeaderProps {
|
||||
title: string[];
|
||||
ready: boolean;
|
||||
ready?: boolean;
|
||||
}
|
||||
|
||||
export default class ItemHeader extends Base<ItemHeaderProps, {}> {
|
||||
|
||||
@@ -7,6 +7,14 @@ import Sorter, {sortByDate, SortInfo} from './sorter';
|
||||
import ResourceSvg from '../art/resourceSvg';
|
||||
import {TODO} from '../utils/types';
|
||||
|
||||
type TableBodyProps<T> = {
|
||||
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<T> = {
|
||||
items?: T[] | null;
|
||||
filter: TODO;
|
||||
colSpan: number;
|
||||
sort: SortInfo;
|
||||
row: (item: T) => ReactNode;
|
||||
}
|
||||
|
||||
export function TableBody<T>({items, filter, colSpan, sort, row}: TableBodyProps<T>) {
|
||||
if (items && sort) {
|
||||
const {field, direction} = sort;
|
||||
|
||||
@@ -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<Metrics>}) {
|
||||
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<Metrics>) {
|
||||
if (!items || !metrics) return null;
|
||||
|
||||
const metricValues = Object.values(metrics);
|
||||
|
||||
@@ -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<Metrics>}) {
|
||||
const totals = getNodeRamTotals(items, metrics);
|
||||
return (
|
||||
<div className='charts_item'>
|
||||
@@ -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<Metrics>) {
|
||||
if (!items || !metrics) return null;
|
||||
|
||||
const metricValues = Object.values(metrics);
|
||||
|
||||
@@ -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<T extends ApiItem<any, any>> {
|
||||
item: T;
|
||||
onSave: (item: T) => Promise<boolean>;
|
||||
}
|
||||
|
||||
interface SaveButtonStates {
|
||||
showEditor: boolean;
|
||||
}
|
||||
|
||||
export default class SaveButton extends Base<SaveButtonProps, SaveButtonStates> {
|
||||
export default class SaveButton<T extends ApiItem<any, any>> extends Base<SaveButtonProps<T>, SaveButtonStates> {
|
||||
render() {
|
||||
const {onSave, item} = this.props;
|
||||
const {showEditor = null} = this.state || {};
|
||||
|
||||
@@ -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<SorterProps, SorterStates> {
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,11 +102,11 @@ function metricsFactory() {
|
||||
return {
|
||||
nodes: (cb: DataCallback<Metrics[]>) => metrics('/apis/metrics.k8s.io/v1beta1/nodes', cb),
|
||||
node: (name: string, cb: DataCallback<Metrics[]>) => metrics(`/apis/metrics.k8s.io/v1beta1/nodes/${name}`, cb),
|
||||
pods: (namespace: string, cb: DataCallback<Metrics[]>) => metrics(url(namespace), cb),
|
||||
pods: (namespace: string | undefined, cb: DataCallback<Metrics[]>) => metrics(url(namespace), cb),
|
||||
pod: (namespace: string, name: string, cb: DataCallback<Metrics[]>) => 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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ export function apiFactoryWithNamespace<T extends ApiItem<any, any>>(group: stri
|
||||
const apiRoot = getApiRoot(group, version);
|
||||
return {
|
||||
resource: {group, resource},
|
||||
list: (namespace: string, cb: StreamCallback<T[]>, errCb?: ErrorCallback) => streamResults(url(namespace), cb, errCb),
|
||||
list: (namespace: string | undefined, cb: StreamCallback<T[]>, errCb?: ErrorCallback) => streamResults(url(namespace), cb, errCb),
|
||||
get: (namespace: string, name: string, cb: StreamCallback<T>, 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<T extends ApiItem<any, any>>(group: stri
|
||||
scale: includeScale ? apiScaleFactory(apiRoot, resource) : undefined,
|
||||
};
|
||||
|
||||
function url(namespace: string) {
|
||||
function url(namespace?: string) {
|
||||
return namespace ? `${apiRoot}/namespaces/${namespace}/${resource}` : `${apiRoot}/${resource}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import Swagger from 'swagger-parser';
|
||||
import apis from './api';
|
||||
import { TODO } from '../utils/types';
|
||||
|
||||
let docsPromise;
|
||||
let docsPromise: Promise<TODO>;
|
||||
|
||||
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;
|
||||
@@ -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<T extends ApiItem<any, any>>(items?: T[], owner?: ApiItem<any, any>): 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<T extends ApiItem<any, any>>(items?: T[], owners?: ApiItem<any, any>[]): 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));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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<any, any>[], 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<Metrics>, 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<Metrics>, 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<Metrics>, 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<Metrics>, 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]);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,31 @@ export type TODO = any;
|
||||
|
||||
export type ApiItem<TSpec, TStatus> = {
|
||||
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<undefined, undefined> {
|
||||
containers: Container[];
|
||||
resources?: {
|
||||
requests?: {
|
||||
cpu?: string;
|
||||
memory?: string;
|
||||
}
|
||||
}
|
||||
usage: MetricsUsage
|
||||
}
|
||||
|
||||
interface NamespaceStatus {
|
||||
@@ -37,20 +73,50 @@ interface NamespaceStatus {
|
||||
export interface Namespace extends ApiItem<undefined, NamespaceStatus> {
|
||||
}
|
||||
|
||||
export interface Node extends ApiItem<undefined, undefined> {
|
||||
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<NodeSpec, NodeStatus> {
|
||||
|
||||
}
|
||||
|
||||
export interface K8sEvent extends ApiItem<undefined, undefined>{
|
||||
export interface K8sEvent extends ApiItem<undefined, undefined> {
|
||||
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<PodSpec, PodStatus>{
|
||||
|
||||
@@ -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<T extends ApiItem<any, any>> {
|
||||
body: T;
|
||||
onSave: (item: T) => Promise<boolean>;
|
||||
onRequestClose: () => void;
|
||||
}
|
||||
|
||||
type State = {
|
||||
showDocs: boolean;
|
||||
yaml?: string;
|
||||
properties?: TODO;
|
||||
}
|
||||
|
||||
export default class EditorModal<T extends ApiItem<any, any>> extends Base<Props<T>, 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});
|
||||
@@ -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();
|
||||
|
||||
@@ -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<Props, State> {
|
||||
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 {
|
||||
)}
|
||||
<div className='charts_itemLabel'>Uptime</div>
|
||||
</div>
|
||||
|
||||
{/*
|
||||
// @ts-ignore */}
|
||||
<NodeCpuChart items={item && [item]} metrics={metrics && [metrics]} />
|
||||
|
||||
{/*
|
||||
// @ts-ignore */}
|
||||
<NodeRamChart items={item && [item]} metrics={metrics && [metrics]} />
|
||||
</ChartsContainer>
|
||||
|
||||
@@ -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) => (
|
||||
<div key={key}> <span>{key}</span> • <span title={key}> {effect} </span></div>
|
||||
));
|
||||
}
|
||||
|
||||
function getUptime({status}) {
|
||||
function getUptime({status}: Node) {
|
||||
const ready = status.conditions.find(y => y.type === 'Ready');
|
||||
if (!ready) return 'N/A';
|
||||
|
||||
@@ -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 || {};
|
||||
@@ -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<Props, State> {
|
||||
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 {
|
||||
</a>
|
||||
|
||||
<SaveButton
|
||||
item={item}
|
||||
item={item!}
|
||||
onSave={x => 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;
|
||||
}
|
||||
@@ -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})),
|
||||
Reference in New Issue
Block a user