1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Implement KubeObjectStatusText extension API (#1188)

Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com>
This commit is contained in:
Lauri Nevala 2020-11-11 08:57:15 +02:00 committed by GitHub
parent d15692a50e
commit 85c4a10114
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 4050 additions and 66 deletions

View File

@ -0,0 +1,8 @@
install-deps:
npm install
build: install-deps
npm run build
test:
npm run test

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
{
"name": "kube-object-event-status",
"version": "0.1.0",
"description": "Adds kube object status from events",
"renderer": "dist/renderer.js",
"lens": {
"metadata": {},
"styles": []
},
"scripts": {
"build": "webpack --config webpack.config.js",
"dev": "npm run build --watch",
"test": "echo NO TESTS"
},
"dependencies": {},
"devDependencies": {
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
"ts-loader": "^8.0.4",
"typescript": "^4.0.3",
"webpack": "^4.44.2",
"mobx": "^5.15.5",
"react": "^16.13.1"
}
}

View File

@ -0,0 +1,42 @@
import { LensRendererExtension, K8sApi } from "@k8slens/extensions";
import { resolveStatus, resolveStatusForCronJobs, resolveStatusForPods } from "./src/resolver"
export default class EventResourceStatusRendererExtension extends LensRendererExtension {
kubeObjectStatusTexts = [
{
kind: "Pod",
apiVersions: ["v1"],
resolve: (pod: K8sApi.Pod) => resolveStatusForPods(pod)
},
{
kind: "ReplicaSet",
apiVersions: ["v1"],
resolve: (replicaSet: K8sApi.ReplicaSet) => resolveStatus(replicaSet)
},
{
kind: "Deployment",
apiVersions: ["apps/v1"],
resolve: (deployment: K8sApi.Deployment) => resolveStatus(deployment)
},
{
kind: "StatefulSet",
apiVersions: ["apps/v1"],
resolve: (statefulSet: K8sApi.StatefulSet) => resolveStatus(statefulSet)
},
{
kind: "DaemonSet",
apiVersions: ["apps/v1"],
resolve: (daemonSet: K8sApi.DaemonSet) => resolveStatus(daemonSet)
},
{
kind: "Job",
apiVersions: ["batch/v1"],
resolve: (job: K8sApi.Job) => resolveStatus(job)
},
{
kind: "CronJob",
apiVersions: ["batch/v1"],
resolve: (cronJob: K8sApi.CronJob) => resolveStatusForCronJobs(cronJob)
},
]
}

View File

@ -0,0 +1,52 @@
import { K8sApi } from "@k8slens/extensions";
export function resolveStatus(object: K8sApi.KubeObject): K8sApi.KubeObjectStatus {
const eventStore = K8sApi.apiManager.getStore(K8sApi.eventApi)
const events = (eventStore as K8sApi.EventStore).getEventsByObject(object);
let warnings = events.filter(evt => evt.isWarning());
if (!events.length || !warnings.length) {
return null;
}
const event = [...warnings, ...events][0]; // get latest event
return {
level: K8sApi.KubeObjectStatusLevel.WARNING,
text: `${event.message}`,
timestamp: event.metadata.creationTimestamp
}
}
export function resolveStatusForPods(pod: K8sApi.Pod): K8sApi.KubeObjectStatus {
if (!pod.hasIssues()) {
return null
}
const eventStore = K8sApi.apiManager.getStore(K8sApi.eventApi)
const events = (eventStore as K8sApi.EventStore).getEventsByObject(pod);
let warnings = events.filter(evt => evt.isWarning());
if (!events.length || !warnings.length) {
return null;
}
const event = [...warnings, ...events][0]; // get latest event
return {
level: K8sApi.KubeObjectStatusLevel.WARNING,
text: `${event.message}`,
timestamp: event.metadata.creationTimestamp
}
}
export function resolveStatusForCronJobs(cronJob: K8sApi.CronJob): K8sApi.KubeObjectStatus {
const eventStore = K8sApi.apiManager.getStore(K8sApi.eventApi)
let events = (eventStore as K8sApi.EventStore).getEventsByObject(cronJob);
let warnings = events.filter(evt => evt.isWarning());
if (cronJob.isNeverRun()) {
events = events.filter(event => event.reason != "FailedNeedsStart");
}
if (!events.length || !warnings.length) {
return null;
}
const event = [...warnings, ...events][0]; // get latest event
return {
level: K8sApi.KubeObjectStatusLevel.WARNING,
text: `${event.message}`,
timestamp: event.metadata.creationTimestamp
}
}

View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"outDir": "dist",
"module": "CommonJS",
"target": "ES2017",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"moduleResolution": "Node",
"sourceMap": false,
"declaration": false,
"strict": false,
"noImplicitAny": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"jsx": "react"
},
"include": [
"./*.ts",
"./*.tsx"
],
"exclude": [
"node_modules",
"*.js"
]
}

View File

@ -0,0 +1,35 @@
const path = require('path');
module.exports = [
{
entry: './renderer.tsx',
context: __dirname,
target: "electron-renderer",
mode: "production",
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
externals: [
{
"@k8slens/extensions": "var global.LensExtensions",
"react": "var global.React",
"mobx": "var global.Mobx"
}
],
resolve: {
extensions: [ '.tsx', '.ts', '.js' ],
},
output: {
libraryTarget: "commonjs2",
globalObject: "this",
filename: 'renderer.js',
path: path.resolve(__dirname, 'dist'),
},
},
];

View File

@ -192,7 +192,8 @@
"node-menu",
"metrics-cluster-feature",
"license-menu-item",
"support-page"
"support-page",
"kube-object-event-status"
]
},
"dependencies": {

View File

@ -77,7 +77,9 @@ export class ExtensionLoader {
registries.clusterPageRegistry.add(...extension.clusterPages),
registries.kubeObjectMenuRegistry.add(...extension.kubeObjectMenuItems),
registries.kubeObjectDetailRegistry.add(...extension.kubeObjectDetailItems),
]);
registries.kubeObjectStatusRegistry.add(...extension.kubeObjectStatusTexts)
])
}
protected autoInitExtensions(register: (ext: LensExtension) => Function[]) {

View File

@ -1,7 +1,7 @@
import type {
AppPreferenceRegistration, ClusterFeatureRegistration,
KubeObjectMenuRegistration, KubeObjectDetailRegistration,
PageRegistration, StatusBarRegistration
PageRegistration, StatusBarRegistration, KubeObjectStatusRegistration
} from "./registries"
import { observable } from "mobx";
import { LensExtension } from "./lens-extension"
@ -9,6 +9,7 @@ import { LensExtension } from "./lens-extension"
export class LensRendererExtension extends LensExtension {
@observable.shallow globalPages: PageRegistration[] = []
@observable.shallow clusterPages: PageRegistration[] = []
@observable.shallow kubeObjectStatusTexts: KubeObjectStatusRegistration[] = []
@observable.shallow appPreferences: AppPreferenceRegistration[] = []
@observable.shallow clusterFeatures: ClusterFeatureRegistration[] = []
@observable.shallow statusBarItems: StatusBarRegistration[] = []

View File

@ -7,3 +7,4 @@ export * from "./status-bar-registry"
export * from "./kube-object-detail-registry";
export * from "./kube-object-menu-registry";
export * from "./cluster-feature-registry"
export * from "./kube-object-status-registry"

View File

@ -0,0 +1,18 @@
import { KubeObject, KubeObjectStatus } from "../renderer-api/k8s-api";
import { BaseRegistry } from "./base-registry";
export interface KubeObjectStatusRegistration {
kind: string;
apiVersions: string[];
resolve: (object: KubeObject) => KubeObjectStatus;
}
export class KubeObjectStatusRegistry extends BaseRegistry<KubeObjectStatusRegistration> {
getItemsForKind(kind: string, apiVersion: string) {
return this.items.filter((item) => {
return item.kind === kind && item.apiVersions.includes(apiVersion)
})
}
}
export const kubeObjectStatusRegistry = new KubeObjectStatusRegistry();

View File

@ -2,6 +2,7 @@ export { isAllowedResource } from "../../common/rbac"
export { apiManager } from "../../renderer/api/api-manager";
export { KubeObjectStore } from "../../renderer/kube-object.store"
export { KubeApi, forCluster, IKubeApiCluster } from "../../renderer/api/kube-api";
export type { EventStore } from "../../renderer/components/+events/event.store"
export { KubeObject } from "../../renderer/api/kube-object";
export { Pod, podsApi, IPodContainer, IPodContainerStatus } from "../../renderer/api/endpoints";
export { Node, nodesApi } from "../../renderer/api/endpoints";
@ -12,6 +13,7 @@ export { Job, jobApi } from "../../renderer/api/endpoints";
export { CronJob, cronJobApi } from "../../renderer/api/endpoints";
export { ConfigMap, configMapApi } from "../../renderer/api/endpoints";
export { Secret, secretsApi, ISecretRef } from "../../renderer/api/endpoints";
export { ReplicaSet, replicaSetApi } from "../../renderer/api/endpoints";
export { ResourceQuota, resourceQuotaApi } from "../../renderer/api/endpoints";
export { HorizontalPodAutoscaler, hpaApi } from "../../renderer/api/endpoints";
export { PodDisruptionBudget, pdbApi } from "../../renderer/api/endpoints";
@ -30,3 +32,4 @@ export { RoleBinding, roleBindingApi } from "../../renderer/api/endpoints";
export { ClusterRole, clusterRoleApi } from "../../renderer/api/endpoints";
export { ClusterRoleBinding, clusterRoleBindingApi } from "../../renderer/api/endpoints";
export { CustomResourceDefinition, crdApi } from "../../renderer/api/endpoints";
export { KubeObjectStatus, KubeObjectStatusLevel} from "./kube-object-status"

View File

@ -0,0 +1,11 @@
export type KubeObjectStatus = {
level: KubeObjectStatusLevel;
text: string;
timestamp?: string;
}
export enum KubeObjectStatusLevel {
INFO = 1,
WARNING = 2,
CRITICAL = 3
}

View File

@ -6,6 +6,10 @@
flex: 1.5;
}
&.warning {
@include table-cell-warning;
}
&.metrics {
flex: 1.5;
}

View File

@ -4,14 +4,13 @@ import React from "react";
import { observer } from "mobx-react";
import { RouteComponentProps } from "react-router";
import { Trans } from "@lingui/macro";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { KubeObjectListLayout } from "../kube-object";
import { IHpaRouteParams } from "./hpa.route";
import { HorizontalPodAutoscaler, hpaApi } from "../../api/endpoints/hpa.api";
import { HorizontalPodAutoscaler } from "../../api/endpoints/hpa.api";
import { hpaStore } from "./hpa.store";
import { Badge } from "../badge";
import { cssNames } from "../../utils";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -52,6 +51,7 @@ export class HorizontalPodAutoscalers extends React.Component<Props> {
renderHeaderTitle={<Trans>Horizontal Pod Autoscalers</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Metrics</Trans>, className: "metrics" },
{ title: <Trans>Min Pods</Trans>, className: "min-pods", sortBy: sortBy.minPods },
@ -62,6 +62,7 @@ export class HorizontalPodAutoscalers extends React.Component<Props> {
]}
renderTableContents={(hpa: HorizontalPodAutoscaler) => [
hpa.getName(),
<KubeObjectStatusIcon object={hpa} />,
hpa.getNs(),
this.getTargets(hpa),
hpa.getMinPods(),

View File

@ -4,6 +4,10 @@
flex: 2;
}
&.warning {
@include table-cell-warning;
}
&.keys {
flex: 2.5;
}

View File

@ -10,6 +10,7 @@ import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-
import { KubeObjectListLayout } from "../kube-object";
import { IConfigMapsRouteParams } from "./config-maps.route";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -40,12 +41,14 @@ export class ConfigMaps extends React.Component<Props> {
renderHeaderTitle={<Trans>Config Maps</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Keys</Trans>, className: "keys", sortBy: sortBy.keys },
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
]}
renderTableContents={(configMap: ConfigMap) => [
configMap.getName(),
<KubeObjectStatusIcon object={configMap}/>,
configMap.getNs(),
configMap.getKeys().join(", "),
configMap.getAge(),

View File

@ -4,6 +4,10 @@
flex: 2;
}
&.warning {
@include table-cell-warning;
}
&.keys {
flex: 2.5;
}

View File

@ -10,6 +10,7 @@ import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-
import { KubeObjectDetailsProps, KubeObjectListLayout } from "../kube-object";
import { IPodDisruptionBudgetsRouteParams } from "./pod-disruption-budgets.route";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -46,6 +47,7 @@ export class PodDisruptionBudgets extends React.Component<Props> {
renderHeaderTitle={<Trans>Pod Disruption Budgets</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Min Available</Trans>, className: "min-available", sortBy: sortBy.minAvailable },
{ title: <Trans>Max Unavailable</Trans>, className: "max-unavailable", sortBy: sortBy.maxUnavailable },
@ -56,6 +58,7 @@ export class PodDisruptionBudgets extends React.Component<Props> {
renderTableContents={(pdb: PodDisruptionBudget) => {
return [
pdb.getName(),
<KubeObjectStatusIcon object={pdb} />,
pdb.getNs(),
pdb.getMinAvailable(),
pdb.getMaxUnavailable(),

View File

@ -1,2 +1,7 @@
.ResourceQuotas {
.TableCell {
&.warning {
@include table-cell-warning;
}
}
}

View File

@ -11,6 +11,7 @@ import { AddQuotaDialog } from "./add-quota-dialog";
import { resourceQuotaStore } from "./resource-quotas.store";
import { IResourceQuotaRouteParams } from "./resource-quotas.route";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -40,11 +41,13 @@ export class ResourceQuotas extends React.Component<Props> {
renderHeaderTitle={<Trans>Resource Quotas</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
]}
renderTableContents={(resourceQuota: ResourceQuota) => [
resourceQuota.getName(),
<KubeObjectStatusIcon object={resourceQuota}/>,
resourceQuota.getNs(),
resourceQuota.getAge(),
]}

View File

@ -4,6 +4,10 @@
flex: 1.5;
}
&.warning {
@include table-cell-warning;
}
&.labels {
@include table-cell-labels-offsets;
}

View File

@ -12,6 +12,7 @@ import { KubeObjectListLayout } from "../kube-object";
import { Badge } from "../badge";
import { secretsStore } from "./secrets.store";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -47,6 +48,7 @@ export class Secrets extends React.Component<Props> {
renderHeaderTitle={<Trans>Secrets</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Labels</Trans>, className: "labels", sortBy: sortBy.labels },
{ title: <Trans>Keys</Trans>, className: "keys", sortBy: sortBy.keys },
@ -55,6 +57,7 @@ export class Secrets extends React.Component<Props> {
]}
renderTableContents={(secret: Secret) => [
secret.getName(),
<KubeObjectStatusIcon object={secret} />,
secret.getNs(),
secret.getLabels().map(label => <Badge key={label} label={label}/>),
secret.getKeys().join(", "),

View File

@ -4,6 +4,10 @@
flex: 2;
}
&.warning {
@include table-cell-warning;
}
&.labels {
flex: 4;
@include table-cell-labels-offsets;

View File

@ -2,16 +2,15 @@ import "./namespaces.scss"
import React from "react";
import { Trans } from "@lingui/macro";
import { Namespace, namespacesApi, NamespaceStatus } from "../../api/endpoints";
import { Namespace, NamespaceStatus } from "../../api/endpoints";
import { AddNamespaceDialog } from "./add-namespace-dialog";
import { TabLayout } from "../layout/tab-layout";
import { Badge } from "../badge";
import { RouteComponentProps } from "react-router";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { KubeObjectListLayout } from "../kube-object";
import { INamespacesRouteParams } from "./namespaces.route";
import { namespaceStore } from "./namespace.store";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -43,12 +42,14 @@ export class Namespaces extends React.Component<Props> {
renderHeaderTitle={<Trans>Namespaces</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Labels</Trans>, className: "labels", sortBy: sortBy.labels },
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
{ title: <Trans>Status</Trans>, className: "status", sortBy: sortBy.status },
]}
renderTableContents={(item: Namespace) => [
item.getName(),
<KubeObjectStatusIcon object={item} />,
item.getLabels().map(label => <Badge key={label} label={label}/>),
item.getAge(),
{ title: item.getStatus(), className: item.getStatus().toLowerCase() },

View File

@ -3,5 +3,9 @@
&.endpoints {
flex-grow: 5.0;
}
&.warning {
@include table-cell-warning;
}
}
}

View File

@ -4,12 +4,11 @@ import React from "react"
import { observer } from "mobx-react";
import { RouteComponentProps } from "react-router-dom"
import { EndpointRouteParams } from "./endpoints.route"
import { Endpoint, endpointApi } from "../../api/endpoints/endpoint.api"
import { Endpoint } from "../../api/endpoints/endpoint.api"
import { endpointStore } from "./endpoints.store";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { KubeObjectListLayout } from "../kube-object";
import { Trans } from "@lingui/macro";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -37,12 +36,14 @@ export class Endpoints extends React.Component<Props> {
renderHeaderTitle={<Trans>Endpoints</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Endpoints</Trans>, className: "endpoints" },
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
]}
renderTableContents={(endpoint: Endpoint) => [
endpoint.getName(),
<KubeObjectStatusIcon object={endpoint} />,
endpoint.getNs(),
endpoint.toString(),
endpoint.getAge(),

View File

@ -3,5 +3,9 @@
&.rules {
flex-grow: 3.0;
}
&.warning {
@include table-cell-warning;
}
}
}

View File

@ -4,12 +4,11 @@ import React from "react"
import { observer } from "mobx-react";
import { RouteComponentProps } from "react-router-dom"
import { IngressRouteParams } from "./ingresses.route"
import { Ingress, ingressApi } from "../../api/endpoints/ingress.api"
import { Ingress } from "../../api/endpoints/ingress.api"
import { ingressStore } from "./ingress.store";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { KubeObjectListLayout } from "../kube-object";
import { Trans } from "@lingui/macro";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -38,6 +37,7 @@ export class Ingresses extends React.Component<Props> {
renderHeaderTitle={<Trans>Ingresses</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>LoadBalancers</Trans>, className: "loadbalancers" },
{ title: <Trans>Rules</Trans>, className: "rules" },
@ -45,6 +45,7 @@ export class Ingresses extends React.Component<Props> {
]}
renderTableContents={(ingress: Ingress) => [
ingress.getName(),
<KubeObjectStatusIcon object={ingress} />,
ingress.getNs(),
ingress.getLoadBalancers().map(lb => <p key={lb}>{lb}</p>),
ingress.getRoutes().map(route => <p key={route}>{route}</p>),

View File

@ -1,2 +1,7 @@
.NetworkPolicies {
.TableCell {
&.warning {
@include table-cell-warning;
}
}
}

View File

@ -4,12 +4,11 @@ import React from "react";
import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { RouteComponentProps } from "react-router-dom";
import { NetworkPolicy, networkPolicyApi } from "../../api/endpoints/network-policy.api";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { NetworkPolicy } from "../../api/endpoints/network-policy.api";
import { KubeObjectListLayout } from "../kube-object";
import { INetworkPoliciesRouteParams } from "./network-policies.route";
import { networkPolicyStore } from "./network-policy.store";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -37,12 +36,14 @@ export class NetworkPolicies extends React.Component<Props> {
renderHeaderTitle={<Trans>Network Policies</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Policy Types</Trans>, className: "type" },
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
]}
renderTableContents={(item: NetworkPolicy) => [
item.getName(),
<KubeObjectStatusIcon object={item} />,
item.getNs(),
item.getTypes().join(", "),
item.getAge(),

View File

@ -4,6 +4,10 @@
flex: 0.6;
}
&.warning {
@include table-cell-warning;
}
&.age {
flex: 0.4;
}

View File

@ -5,12 +5,11 @@ import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { RouteComponentProps } from "react-router";
import { IServicesRouteParams } from "./services.route";
import { Service, serviceApi } from "../../api/endpoints/service.api";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { Service } from "../../api/endpoints/service.api";
import { KubeObjectListLayout } from "../kube-object";
import { Badge } from "../badge";
import { serviceStore } from "./services.store";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -50,6 +49,7 @@ export class Services extends React.Component<Props> {
renderHeaderTitle={<Trans>Services</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Type</Trans>, className: "type", sortBy: sortBy.type },
{ title: <Trans>Cluster IP</Trans>, className: "clusterIp", sortBy: sortBy.clusterIp, },
@ -61,6 +61,7 @@ export class Services extends React.Component<Props> {
]}
renderTableContents={(service: Service) => [
service.getName(),
<KubeObjectStatusIcon object={service} />,
service.getNs(),
service.getType(),
service.getClusterIp(),

View File

@ -7,6 +7,10 @@
text-overflow: ellipsis;
}
&.warning {
@include table-cell-warning;
}
&.cpu {
flex: 1.0;
align-self: center;

View File

@ -16,6 +16,7 @@ import { bytesToUnits } from "../../utils/convertMemory";
import { Tooltip, TooltipPosition } from "../tooltip";
import kebabCase from "lodash/kebabCase";
import upperFirst from "lodash/upperFirst";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -148,6 +149,7 @@ export class Nodes extends React.Component<Props> {
renderHeaderTitle={<Trans>Nodes</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>CPU</Trans>, className: "cpu", sortBy: sortBy.cpu },
{ title: <Trans>Memory</Trans>, className: "memory", sortBy: sortBy.memory },
{ title: <Trans>Disk</Trans>, className: "disk", sortBy: sortBy.disk },
@ -161,6 +163,7 @@ export class Nodes extends React.Component<Props> {
const tooltipId = `node-taints-${node.getId()}`;
return [
node.getName(),
<KubeObjectStatusIcon object={node} />,
this.renderCpuUsage(node),
this.renderMemoryUsage(node),
this.renderDiskUsage(node),

View File

@ -4,6 +4,10 @@
&.volumes {
flex-grow: 2;
}
&.warning {
@include table-cell-warning;
}
}
}
}

View File

@ -4,10 +4,9 @@ import React from "react";
import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { KubeObjectListLayout } from "../kube-object";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { podSecurityPoliciesStore } from "./pod-security-policies.store";
import { PodSecurityPolicy, pspApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -38,6 +37,7 @@ export class PodSecurityPolicies extends React.Component {
renderHeaderTitle={<Trans>Pod Security Policies</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Privileged</Trans>, className: "privileged", sortBy: sortBy.privileged },
{ title: <Trans>Volumes</Trans>, className: "volumes", sortBy: sortBy.volumes },
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
@ -45,6 +45,7 @@ export class PodSecurityPolicies extends React.Component {
renderTableContents={(item: PodSecurityPolicy) => {
return [
item.getName(),
<KubeObjectStatusIcon object={item} />,
item.isPrivileged() ? <Trans>Yes</Trans> : <Trans>No</Trans>,
item.getVolumes().join(", "),
item.getAge(),

View File

@ -3,5 +3,9 @@
&.is-default {
flex: .5;
}
&.warning {
@include table-cell-warning;
}
}
}

View File

@ -5,11 +5,10 @@ import { RouteComponentProps } from "react-router-dom";
import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { StorageClass, storageClassApi } from "../../api/endpoints/storage-class.api";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { KubeObjectListLayout } from "../kube-object";
import { IStorageClassesRouteParams } from "./storage-classes.route";
import { storageClassStore } from "./storage-class.store";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -41,6 +40,7 @@ export class StorageClasses extends React.Component<Props> {
renderHeaderTitle={<Trans>Storage Classes</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Provisioner</Trans>, className: "provisioner", sortBy: sortBy.provisioner },
{ title: <Trans>Reclaim Policy</Trans>, className: "reclaim-policy", sortBy: sortBy.reclaimPolicy },
{ title: <Trans>Default</Trans>, className: "is-default" },
@ -48,6 +48,7 @@ export class StorageClasses extends React.Component<Props> {
]}
renderTableContents={(storageClass: StorageClass) => [
storageClass.getName(),
<KubeObjectStatusIcon object={storageClass} />,
storageClass.provisioner,
storageClass.getReclaimPolicy(),
storageClass.isDefault() ? <Trans>Yes</Trans> : null,

View File

@ -6,6 +6,10 @@
flex-grow: 3;
}
&.warning {
@include table-cell-warning;
}
&.storage {
flex: 0.6;
}

View File

@ -5,8 +5,7 @@ import { observer } from "mobx-react";
import { Link, RouteComponentProps } from "react-router-dom";
import { Trans } from "@lingui/macro";
import { volumeClaimStore } from "./volume-claim.store";
import { PersistentVolumeClaim, pvcApi } from "../../api/endpoints/persistent-volume-claims.api";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { PersistentVolumeClaim } from "../../api/endpoints/persistent-volume-claims.api";
import { podsStore } from "../+workloads-pods/pods.store";
import { KubeObjectListLayout } from "../kube-object";
import { IVolumeClaimsRouteParams } from "./volume-claims.route";
@ -14,7 +13,7 @@ import { unitsToBytes } from "../../utils/convertMemory";
import { stopPropagation } from "../../utils";
import { getDetailsUrl } from "../../navigation";
import { storageClassApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -53,6 +52,7 @@ export class PersistentVolumeClaims extends React.Component<Props> {
renderHeaderTitle={<Trans>Persistent Volume Claims</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Storage class</Trans>, className: "storageClass", sortBy: sortBy.storageClass },
{ title: <Trans>Size</Trans>, className: "size", sortBy: sortBy.size },
@ -68,6 +68,7 @@ export class PersistentVolumeClaims extends React.Component<Props> {
}));
return [
pvc.getName(),
<KubeObjectStatusIcon object={pvc} />,
pvc.getNs(),
<Link to={storageClassDetailsUrl} onClick={stopPropagation}>
{storageClassName}

View File

@ -6,6 +6,10 @@
flex-grow: 3;
}
&.warning {
@include table-cell-warning;
}
&.storage {
flex: 0.6;
}

View File

@ -4,15 +4,14 @@ import React from "react";
import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { Link, RouteComponentProps } from "react-router-dom";
import { PersistentVolume, persistentVolumeApi } from "../../api/endpoints/persistent-volume.api";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { PersistentVolume } from "../../api/endpoints/persistent-volume.api";
import { KubeObjectListLayout } from "../kube-object";
import { IVolumesRouteParams } from "./volumes.route";
import { stopPropagation } from "../../utils";
import { getDetailsUrl } from "../../navigation";
import { volumesStore } from "./volumes.store";
import { pvcApi, storageClassApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -46,6 +45,7 @@ export class PersistentVolumes extends React.Component<Props> {
renderHeaderTitle={<Trans>Persistent Volumes</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Storage Class</Trans>, className: "storageClass", sortBy: sortBy.storageClass },
{ title: <Trans>Capacity</Trans>, className: "capacity", sortBy: sortBy.capacity },
{ title: <Trans>Claim</Trans>, className: "claim" },
@ -59,6 +59,7 @@ export class PersistentVolumes extends React.Component<Props> {
}));
return [
volume.getName(),
<KubeObjectStatusIcon object={volume} />,
<Link to={storageClassDetailsUrl} onClick={stopPropagation}>
{storageClassName}
</Link>,

View File

@ -2,4 +2,10 @@
.help-icon {
margin-left: $margin / 2;
}
.TableCell {
&.warning {
@include table-cell-warning;
}
}
}

View File

@ -4,15 +4,12 @@ import React from "react";
import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { RouteComponentProps } from "react-router";
import { Icon } from "../icon";
import { IRoleBindingsRouteParams } from "../+user-management/user-management.route";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { clusterRoleBindingApi, RoleBinding, roleBindingApi } from "../../api/endpoints";
import { RoleBinding } from "../../api/endpoints";
import { roleBindingsStore } from "./role-bindings.store";
import { KubeObjectListLayout } from "../kube-object";
import { AddRoleBindingDialog } from "./add-role-binding-dialog";
import { KubeObject } from "../../api/kube-object";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -44,12 +41,14 @@ export class RoleBindings extends React.Component<Props> {
renderHeaderTitle={<Trans>Role Bindings</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Bindings</Trans>, className: "bindings", sortBy: sortBy.bindings },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
]}
renderTableContents={(binding: RoleBinding) => [
binding.getName(),
<KubeObjectStatusIcon object={binding} />,
binding.getSubjectNames(),
binding.getNs() || "-",
binding.getAge(),

View File

@ -2,4 +2,10 @@
.help-icon {
margin-left: $margin / 2;
}
.TableCell {
&.warning {
@include table-cell-warning;
}
}
}

View File

@ -9,6 +9,7 @@ import { rolesStore } from "./roles.store";
import { Role } from "../../api/endpoints";
import { KubeObjectListLayout } from "../kube-object";
import { AddRoleDialog } from "./add-role-dialog";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -38,11 +39,13 @@ export class Roles extends React.Component<Props> {
renderHeaderTitle={<Trans>Roles</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
]}
renderTableContents={(role: Role) => [
role.getName(),
<KubeObjectStatusIcon object={role} />,
role.getNs() || "-",
role.getAge(),
]}

View File

@ -1,2 +1,7 @@
.ServiceAccounts {
.TableCell {
&.warning {
@include table-cell-warning;
}
}
}

View File

@ -14,6 +14,7 @@ import { IServiceAccountsRouteParams } from "../+user-management";
import { serviceAccountsStore } from "./service-accounts.store";
import { CreateServiceAccountDialog } from "./create-service-account-dialog";
import { kubeObjectMenuRegistry } from "../../../extensions/registries/kube-object-menu-registry";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -42,11 +43,13 @@ export class ServiceAccounts extends React.Component<Props> {
renderHeaderTitle={<Trans>Service Accounts</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
]}
renderTableContents={(account: ServiceAccount) => [
account.getName(),
<KubeObjectStatusIcon object={account} />,
account.getNs(),
account.getAge(),
]}

View File

@ -13,10 +13,10 @@ import { eventStore } from "../+events/event.store";
import { KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { ICronJobsRouteParams } from "../+workloads";
import { KubeObjectListLayout } from "../kube-object";
import { KubeEventIcon } from "../+events/kube-event-icon";
import { _i18n } from "../../i18n";
import { CronJobTriggerDialog } from "./cronjob-trigger-dialog";
import { kubeObjectMenuRegistry } from "../../../extensions/registries/kube-object-menu-registry";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -62,11 +62,7 @@ export class CronJobs extends React.Component<Props> {
]}
renderTableContents={(cronJob: CronJob) => [
cronJob.getName(),
<KubeEventIcon object={cronJob} filterEvents={events => {
if (!cronJob.isNeverRun()) return events;
return events.filter(event => event.reason != "FailedNeedsStart");
}
}/>,
<KubeObjectStatusIcon object={cronJob} />,
cronJob.getNs(),
cronJob.isNeverRun() ? <Trans>never</Trans> : cronJob.getSchedule(),
cronJob.getSuspendFlag(),

View File

@ -3,8 +3,7 @@ import "./daemonsets.scss";
import React from "react";
import { observer } from "mobx-react";
import { RouteComponentProps } from "react-router";
import { DaemonSet, daemonSetApi } from "../../api/endpoints";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { DaemonSet } from "../../api/endpoints";
import { eventStore } from "../+events/event.store";
import { daemonSetStore } from "./daemonsets.store";
import { podsStore } from "../+workloads-pods/pods.store";
@ -13,8 +12,7 @@ import { KubeObjectListLayout } from "../kube-object";
import { IDaemonSetsRouteParams } from "../+workloads";
import { Trans } from "@lingui/macro";
import { Badge } from "../badge";
import { KubeEventIcon } from "../+events/kube-event-icon";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -66,7 +64,7 @@ export class DaemonSets extends React.Component<Props> {
daemonSet.getName(),
daemonSet.getNs(),
this.getPodsLength(daemonSet),
<KubeEventIcon object={daemonSet}/>,
<KubeObjectStatusIcon object={daemonSet}/>,
this.renderNodeSelector(daemonSet),
daemonSet.getAge(),
]}

View File

@ -21,9 +21,8 @@ import { _i18n } from "../../i18n";
import { cssNames } from "../../utils";
import kebabCase from "lodash/kebabCase";
import orderBy from "lodash/orderBy";
import { KubeEventIcon } from "../+events/kube-event-icon";
import { kubeObjectMenuRegistry } from "../../../extensions/registries/kube-object-menu-registry";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import { Notifications } from "../notifications";
enum sortBy {
@ -72,19 +71,19 @@ export class Deployments extends React.Component<Props> {
renderHeaderTitle={<Trans>Deployments</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ className: "warning" },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Pods</Trans>, className: "pods" },
{ title: <Trans>Replicas</Trans>, className: "replicas", sortBy: sortBy.replicas },
{ className: "warning" },
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
{ title: <Trans>Conditions</Trans>, className: "conditions", sortBy: sortBy.condition },
]}
renderTableContents={(deployment: Deployment) => [
deployment.getName(),
<KubeObjectStatusIcon object={deployment}/>,
deployment.getNs(),
this.renderPods(deployment),
deployment.getReplicas(),
<KubeEventIcon object={deployment}/>,
deployment.getAge(),
this.renderConditions(deployment),
]}

View File

@ -7,13 +7,11 @@ import { Trans } from "@lingui/macro";
import { podsStore } from "../+workloads-pods/pods.store";
import { jobStore } from "./job.store";
import { eventStore } from "../+events/event.store";
import { Job, jobApi } from "../../api/endpoints/job.api";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { Job } from "../../api/endpoints/job.api";
import { KubeObjectListLayout } from "../kube-object";
import { IJobsRouteParams } from "../+workloads";
import { KubeEventIcon } from "../+events/kube-event-icon";
import kebabCase from "lodash/kebabCase";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -56,7 +54,7 @@ export class Jobs extends React.Component<Props> {
job.getName(),
job.getNs(),
`${job.getCompletions()} / ${job.getDesiredCompletions()}`,
<KubeEventIcon object={job}/>,
<KubeObjectStatusIcon object={job}/>,
job.getAge(),
condition && {
title: condition.type,

View File

@ -7,7 +7,6 @@ import { Trans } from "@lingui/macro";
import { podsStore } from "./pods.store";
import { Pod } from "../../api/endpoints";
import { autobind, bytesToUnits, cssNames, interval, prevDefault } from "../../utils";
import { KubeEventIcon } from "../+events/kube-event-icon";
import { LineProgress } from "../line-progress";
import { KubeObject } from "../../api/kube-object";
import { Table, TableCell, TableHead, TableRow } from "../table";
@ -15,6 +14,7 @@ import { showDetails } from "../../navigation";
import { reaction } from "mobx";
import { Spinner } from "../spinner";
import { DrawerTitle } from "../drawer";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -107,7 +107,7 @@ export class PodDetailsList extends React.Component<Props> {
onClick={prevDefault(() => showDetails(pod.selfLink, false))}
>
<TableCell className="name">{pod.getName()}</TableCell>
<TableCell className="warning">{pod.hasIssues() && <KubeEventIcon object={pod}/>}</TableCell>
<TableCell className="warning"><KubeObjectStatusIcon object={pod}/></TableCell>
<TableCell className="namespace">{pod.getNs()}</TableCell>
<TableCell className="cpu">{this.renderCpuUsage(`cpu-${pod.getId()}`, metrics.cpu)}</TableCell>
<TableCell className="memory">{this.renderMemoryUsage(`memory-${pod.getId()}`, metrics.memory)}</TableCell>

View File

@ -13,12 +13,13 @@ import { KubeObjectListLayout } from "../kube-object";
import { Pod } from "../../api/endpoints";
import { StatusBrick } from "../status-brick";
import { cssNames, stopPropagation } from "../../utils";
import { KubeEventIcon } from "../+events/kube-event-icon";
import { getDetailsUrl } from "../../navigation";
import toPairs from "lodash/toPairs";
import startCase from "lodash/startCase";
import kebabCase from "lodash/kebabCase";
import { lookupApiLink } from "../../api/kube-api";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -100,7 +101,7 @@ export class Pods extends React.Component<Props> {
]}
renderTableContents={(pod: Pod) => [
pod.getName(),
pod.hasIssues() && <KubeEventIcon object={pod}/>,
<KubeObjectStatusIcon object={pod} />,
pod.getNs(),
this.renderContainersStatus(pod),
pod.getRestartsCount(),

View File

@ -19,6 +19,10 @@
flex-grow: 2;
}
&.warning {
@include table-cell-warning;
}
&.namespace {
flex-grow: 1.2;
}

View File

@ -12,6 +12,7 @@ import { DrawerTitle } from "../drawer";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { showDetails } from "../../navigation";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -56,6 +57,7 @@ export class ReplicaSets extends React.Component<Props> {
>
<TableHead>
<TableCell className="name" sortBy={sortBy.name}><Trans>Name</Trans></TableCell>
<TableCell className="warning"/>
<TableCell className="namespace" sortBy={sortBy.namespace}>Namespace</TableCell>
<TableCell className="pods" sortBy={sortBy.pods}><Trans>Pods</Trans></TableCell>
<TableCell className="age" sortBy={sortBy.age}><Trans>Age</Trans></TableCell>
@ -71,6 +73,7 @@ export class ReplicaSets extends React.Component<Props> {
onClick={prevDefault(() => showDetails(replica.selfLink, false))}
>
<TableCell className="name">{replica.getName()}</TableCell>
<TableCell className="warning"><KubeObjectStatusIcon object={replica}/></TableCell>
<TableCell className="namespace">{replica.getNs()}</TableCell>
<TableCell className="pods">{this.getPodsLength(replica)}</TableCell>
<TableCell className="age">{replica.getAge()}</TableCell>

View File

@ -4,16 +4,14 @@ import React from "react";
import { observer } from "mobx-react";
import { RouteComponentProps } from "react-router";
import { Trans } from "@lingui/macro";
import { StatefulSet, statefulSetApi } from "../../api/endpoints";
import { StatefulSet } from "../../api/endpoints";
import { podsStore } from "../+workloads-pods/pods.store";
import { statefulSetStore } from "./statefulset.store";
import { nodesStore } from "../+nodes/nodes.store";
import { eventStore } from "../+events/event.store";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { KubeObjectListLayout } from "../kube-object";
import { IStatefulSetsRouteParams } from "../+workloads";
import { KubeEventIcon } from "../+events/kube-event-icon";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
enum sortBy {
name = "name",
@ -57,7 +55,7 @@ export class StatefulSets extends React.Component<Props> {
statefulSet.getName(),
statefulSet.getNs(),
this.getPodsLength(statefulSet),
<KubeEventIcon object={statefulSet}/>,
<KubeObjectStatusIcon object={statefulSet}/>,
statefulSet.getAge(),
]}
/>

View File

@ -172,6 +172,10 @@ a {
color: $colorError;
}
.warning {
color: $colorWarning;
}
.contrast {
color: $textColorAccent;
}

View File

@ -0,0 +1 @@
export * from "./kube-object-status-icon"

View File

@ -0,0 +1,32 @@
.KubeObjectStatusIcon {
&.warning {
color: $golden;
}
}
.KubeObjectStatusTooltip {
white-space: normal;
.level {
padding: 4px;
&:not(:only-child):not(:last-child) {
border-bottom: 1px solid $borderFaintColor;
}
.title {
font-weight: bold;
.Icon {
margin-right: $margin;
}
}
.msg {
margin: $margin / 2
}
.age {
color: $halfGray;
}
}
}

View File

@ -0,0 +1,104 @@
import "./kube-object-status-icon.scss";
import React from "react";
import { Icon } from "../icon";
import { KubeObject } from "../../api/kube-object";
import { cssNames, formatDuration } from "../../utils";
import { KubeObjectStatusRegistration, kubeObjectStatusRegistry } from "../../../extensions/registries/kube-object-status-registry"
import { KubeObjectStatus, KubeObjectStatusLevel } from "../../..//extensions/renderer-api/k8s-api";
import { computed } from "mobx";
interface Props {
object: KubeObject;
}
export class KubeObjectStatusIcon extends React.Component<Props> {
@computed get objectStatuses() {
const { object } = this.props;
const registrations = kubeObjectStatusRegistry.getItemsForKind(object.kind, object.apiVersion)
return registrations.map((item: KubeObjectStatusRegistration) => { return item.resolve(object) }).filter((item: KubeObjectStatus) => !!item)
}
statusClassName(level: number): string {
switch (level) {
case KubeObjectStatusLevel.INFO:
return "info"
case KubeObjectStatusLevel.WARNING:
return "warning"
case KubeObjectStatusLevel.CRITICAL:
return "error"
default:
return "";
}
}
statusTitle(level: number): string {
switch (level) {
case KubeObjectStatusLevel.INFO:
return "Info"
case KubeObjectStatusLevel.WARNING:
return "Warning"
case KubeObjectStatusLevel.CRITICAL:
return "Critical"
default:
return "";
}
}
getAge(timestamp: string) {
if (!timestamp) return ""
const diff = new Date().getTime() - new Date(timestamp).getTime();
return formatDuration(diff, true);
}
renderStatuses(statuses: KubeObjectStatus[], level: number) {
const filteredStatuses = statuses.filter((item) => item.level == level)
return filteredStatuses.length > 0 && (
<div className={cssNames("level", this.statusClassName(level))}>
<span className="title">
{this.statusTitle(level)}
</span>
{ filteredStatuses.map((status, index) =>{
return (
<div key={`kube-resource-status-${level}-${index}`} className={cssNames("status", "msg")}>
- {status.text} <span className="age"> · { this.getAge(status.timestamp) }</span>
</div>
)
})}
</div>
)
}
render() {
const { objectStatuses} = this
if (!objectStatuses.length) return null
const sortedStatuses = objectStatuses.sort((a: KubeObjectStatus, b: KubeObjectStatus) => {
if (a.level < b.level ) {
return 1
}
if (a.level > b.level ) {
return -1
}
return 0
})
const level = this.statusClassName(sortedStatuses[0].level)
return (
<Icon
material={level}
className={cssNames("KubeObjectStatusIcon", level)}
tooltip={{
children: (
<div className="KubeObjectStatusTooltip">
{this.renderStatuses(sortedStatuses, KubeObjectStatusLevel.CRITICAL)}
{this.renderStatuses(sortedStatuses, KubeObjectStatusLevel.WARNING)}
{this.renderStatuses(sortedStatuses, KubeObjectStatusLevel.INFO)}
</div>
)
}}
/>
)
}
}

View File

@ -5,6 +5,7 @@ import { DrawerItem, DrawerItemLabels } from "../drawer";
import { getDetailsUrl } from "../../navigation";
import { lookupApiLink } from "../../api/kube-api";
import { Link } from "react-router-dom";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
export interface KubeObjectMetaProps {
object: KubeObject;
@ -36,7 +37,7 @@ export class KubeObjectMeta extends React.Component<KubeObjectMetaProps> {
{getAge(true, false)} <Trans>ago</Trans> ({creationTimestamp})
</DrawerItem>
<DrawerItem name={<Trans>Name</Trans>} hidden={this.isHidden("name")}>
{getName()}
{getName()} <KubeObjectStatusIcon object={object} />
</DrawerItem>
<DrawerItem name={<Trans>Namespace</Trans>} hidden={this.isHidden("namespace") || !getNs()}>
{getNs()}