mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Improve Table typing and remove annotations on callbacks (#2981)
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
c6a4d55d86
commit
64ac04186b
@ -22,8 +22,9 @@
|
|||||||
import { ingressStore } from "../../components/+network-ingresses/ingress.store";
|
import { ingressStore } from "../../components/+network-ingresses/ingress.store";
|
||||||
import { apiManager } from "../api-manager";
|
import { apiManager } from "../api-manager";
|
||||||
import { KubeApi } from "../kube-api";
|
import { KubeApi } from "../kube-api";
|
||||||
|
import { KubeObject } from "../kube-object";
|
||||||
|
|
||||||
class TestApi extends KubeApi {
|
class TestApi extends KubeApi<KubeObject> {
|
||||||
|
|
||||||
protected async checkPreferredVersion() {
|
protected async checkPreferredVersion() {
|
||||||
return;
|
return;
|
||||||
@ -36,6 +37,7 @@ describe("ApiManager", () => {
|
|||||||
const apiBase = "apis/v1/foo";
|
const apiBase = "apis/v1/foo";
|
||||||
const fallbackApiBase = "/apis/extensions/v1beta1/foo";
|
const fallbackApiBase = "/apis/extensions/v1beta1/foo";
|
||||||
const kubeApi = new TestApi({
|
const kubeApi = new TestApi({
|
||||||
|
objectConstructor: KubeObject,
|
||||||
apiBase,
|
apiBase,
|
||||||
fallbackApiBases: [fallbackApiBase],
|
fallbackApiBases: [fallbackApiBase],
|
||||||
checkPreferredVersion: true,
|
checkPreferredVersion: true,
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { CustomResourceDefinition } from "../endpoints";
|
import { CustomResourceDefinition } from "../endpoints";
|
||||||
import type { IKubeObjectMetadata } from "../kube-object";
|
import type { KubeObjectMetadata } from "../kube-object";
|
||||||
|
|
||||||
describe("Crds", () => {
|
describe("Crds", () => {
|
||||||
describe("getVersion", () => {
|
describe("getVersion", () => {
|
||||||
@ -28,7 +28,7 @@ describe("Crds", () => {
|
|||||||
const crd = new CustomResourceDefinition({
|
const crd = new CustomResourceDefinition({
|
||||||
apiVersion: "foo",
|
apiVersion: "foo",
|
||||||
kind: "CustomResourceDefinition",
|
kind: "CustomResourceDefinition",
|
||||||
metadata: {} as IKubeObjectMetadata,
|
metadata: {} as KubeObjectMetadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
crd.spec = {
|
crd.spec = {
|
||||||
@ -48,7 +48,7 @@ describe("Crds", () => {
|
|||||||
const crd = new CustomResourceDefinition({
|
const crd = new CustomResourceDefinition({
|
||||||
apiVersion: "foo",
|
apiVersion: "foo",
|
||||||
kind: "CustomResourceDefinition",
|
kind: "CustomResourceDefinition",
|
||||||
metadata: {} as IKubeObjectMetadata,
|
metadata: {} as KubeObjectMetadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
crd.spec = {
|
crd.spec = {
|
||||||
@ -73,7 +73,7 @@ describe("Crds", () => {
|
|||||||
const crd = new CustomResourceDefinition({
|
const crd = new CustomResourceDefinition({
|
||||||
apiVersion: "foo",
|
apiVersion: "foo",
|
||||||
kind: "CustomResourceDefinition",
|
kind: "CustomResourceDefinition",
|
||||||
metadata: {} as IKubeObjectMetadata,
|
metadata: {} as KubeObjectMetadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
crd.spec = {
|
crd.spec = {
|
||||||
@ -99,7 +99,7 @@ describe("Crds", () => {
|
|||||||
const crd = new CustomResourceDefinition({
|
const crd = new CustomResourceDefinition({
|
||||||
apiVersion: "foo",
|
apiVersion: "foo",
|
||||||
kind: "CustomResourceDefinition",
|
kind: "CustomResourceDefinition",
|
||||||
metadata: {} as IKubeObjectMetadata,
|
metadata: {} as KubeObjectMetadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
crd.spec = {
|
crd.spec = {
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { KubeApi } from "../kube-api";
|
import { KubeApi } from "../kube-api";
|
||||||
|
import { KubeObject } from "../kube-object";
|
||||||
|
|
||||||
describe("KubeApi", () => {
|
describe("KubeApi", () => {
|
||||||
it("uses url from apiBase if apiBase contains the resource", async () => {
|
it("uses url from apiBase if apiBase contains the resource", async () => {
|
||||||
@ -29,7 +30,7 @@ describe("KubeApi", () => {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
resources: [{
|
resources: [{
|
||||||
name: "ingresses"
|
name: "ingresses"
|
||||||
}] as any []
|
}] as any[]
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
} else if (request.url === "/api-kube/apis/extensions/v1beta1") {
|
} else if (request.url === "/api-kube/apis/extensions/v1beta1") {
|
||||||
@ -38,13 +39,13 @@ describe("KubeApi", () => {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
resources: [{
|
resources: [{
|
||||||
name: "ingresses"
|
name: "ingresses"
|
||||||
}] as any []
|
}] as any[]
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
resources: [] as any []
|
resources: [] as any[]
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -53,6 +54,7 @@ describe("KubeApi", () => {
|
|||||||
const apiBase = "/apis/networking.k8s.io/v1/ingresses";
|
const apiBase = "/apis/networking.k8s.io/v1/ingresses";
|
||||||
const fallbackApiBase = "/apis/extensions/v1beta1/ingresses";
|
const fallbackApiBase = "/apis/extensions/v1beta1/ingresses";
|
||||||
const kubeApi = new KubeApi({
|
const kubeApi = new KubeApi({
|
||||||
|
objectConstructor: KubeObject,
|
||||||
apiBase,
|
apiBase,
|
||||||
fallbackApiBases: [fallbackApiBase],
|
fallbackApiBases: [fallbackApiBase],
|
||||||
checkPreferredVersion: true,
|
checkPreferredVersion: true,
|
||||||
@ -68,7 +70,7 @@ describe("KubeApi", () => {
|
|||||||
if (request.url === "/api-kube/apis/networking.k8s.io/v1") {
|
if (request.url === "/api-kube/apis/networking.k8s.io/v1") {
|
||||||
return {
|
return {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
resources: [] as any []
|
resources: [] as any[]
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
} else if (request.url === "/api-kube/apis/extensions/v1beta1") {
|
} else if (request.url === "/api-kube/apis/extensions/v1beta1") {
|
||||||
@ -76,13 +78,13 @@ describe("KubeApi", () => {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
resources: [{
|
resources: [{
|
||||||
name: "ingresses"
|
name: "ingresses"
|
||||||
}] as any []
|
}] as any[]
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
resources: [] as any []
|
resources: [] as any[]
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -91,6 +93,7 @@ describe("KubeApi", () => {
|
|||||||
const apiBase = "apis/networking.k8s.io/v1/ingresses";
|
const apiBase = "apis/networking.k8s.io/v1/ingresses";
|
||||||
const fallbackApiBase = "/apis/extensions/v1beta1/ingresses";
|
const fallbackApiBase = "/apis/extensions/v1beta1/ingresses";
|
||||||
const kubeApi = new KubeApi({
|
const kubeApi = new KubeApi({
|
||||||
|
objectConstructor: KubeObject,
|
||||||
apiBase,
|
apiBase,
|
||||||
fallbackApiBases: [fallbackApiBase],
|
fallbackApiBases: [fallbackApiBase],
|
||||||
checkPreferredVersion: true,
|
checkPreferredVersion: true,
|
||||||
|
|||||||
@ -22,34 +22,35 @@
|
|||||||
import type { KubeObjectStore } from "../kube-object.store";
|
import type { KubeObjectStore } from "../kube-object.store";
|
||||||
|
|
||||||
import { action, observable, makeObservable } from "mobx";
|
import { action, observable, makeObservable } from "mobx";
|
||||||
import { autoBind } from "../utils";
|
import { autoBind, iter } from "../utils";
|
||||||
import { KubeApi, parseKubeApi } from "./kube-api";
|
import { KubeApi, parseKubeApi } from "./kube-api";
|
||||||
|
import type { KubeObject } from "./kube-object";
|
||||||
|
|
||||||
export class ApiManager {
|
export class ApiManager {
|
||||||
private apis = observable.map<string, KubeApi>();
|
private apis = observable.map<string, KubeApi<KubeObject>>();
|
||||||
private stores = observable.map<string, KubeObjectStore>();
|
private stores = observable.map<string, KubeObjectStore<KubeObject>>();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
autoBind(this);
|
autoBind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) {
|
getApi(pathOrCallback: string | ((api: KubeApi<KubeObject>) => boolean)) {
|
||||||
if (typeof pathOrCallback === "string") {
|
if (typeof pathOrCallback === "string") {
|
||||||
return this.apis.get(pathOrCallback) || this.apis.get(parseKubeApi(pathOrCallback).apiBase);
|
return this.apis.get(pathOrCallback) || this.apis.get(parseKubeApi(pathOrCallback).apiBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.from(this.apis.values()).find(pathOrCallback ?? (() => true));
|
return iter.find(this.apis.values(), pathOrCallback ?? (() => true));
|
||||||
}
|
}
|
||||||
|
|
||||||
getApiByKind(kind: string, apiVersion: string) {
|
getApiByKind(kind: string, apiVersion: string) {
|
||||||
return Array.from(this.apis.values()).find((api) => api.kind === kind && api.apiVersionWithGroup === apiVersion);
|
return iter.find(this.apis.values(), api => api.kind === kind && api.apiVersionWithGroup === apiVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerApi(apiBase: string, api: KubeApi) {
|
registerApi(apiBase: string, api: KubeApi<KubeObject>) {
|
||||||
if (!this.apis.has(apiBase)) {
|
if (!this.apis.has(apiBase)) {
|
||||||
this.stores.forEach((store) => {
|
this.stores.forEach((store) => {
|
||||||
if(store.api === api) {
|
if (store.api === api) {
|
||||||
this.stores.set(apiBase, store);
|
this.stores.set(apiBase, store);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -58,19 +59,19 @@ export class ApiManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected resolveApi(api?: string | KubeApi): KubeApi | undefined {
|
protected resolveApi<K extends KubeObject>(api?: string | KubeApi<K>): KubeApi<K> | undefined {
|
||||||
if (!api) {
|
if (!api) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof api === "string") {
|
if (typeof api === "string") {
|
||||||
return this.getApi(api);
|
return this.getApi(api) as KubeApi<K>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return api;
|
return api;
|
||||||
}
|
}
|
||||||
|
|
||||||
unregisterApi(api: string | KubeApi) {
|
unregisterApi(api: string | KubeApi<KubeObject>) {
|
||||||
if (typeof api === "string") this.apis.delete(api);
|
if (typeof api === "string") this.apis.delete(api);
|
||||||
else {
|
else {
|
||||||
const apis = Array.from(this.apis.entries());
|
const apis = Array.from(this.apis.entries());
|
||||||
@ -81,13 +82,13 @@ export class ApiManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
registerStore(store: KubeObjectStore, apis: KubeApi[] = [store.api]) {
|
registerStore(store: KubeObjectStore<KubeObject>, apis: KubeApi<KubeObject>[] = [store.api]) {
|
||||||
apis.forEach(api => {
|
apis.forEach(api => {
|
||||||
this.stores.set(api.apiBase, store);
|
this.stores.set(api.apiBase, store);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getStore<S extends KubeObjectStore>(api: string | KubeApi): S {
|
getStore<S extends KubeObjectStore<KubeObject>>(api: string | KubeApi<KubeObject>): S | undefined {
|
||||||
return this.stores.get(this.resolveApi(api)?.apiBase) as S;
|
return this.stores.get(this.resolveApi(api)?.apiBase) as S;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export const resourceApplierApi = {
|
|||||||
"kubectl.kubernetes.io/last-applied-configuration"
|
"kubectl.kubernetes.io/last-applied-configuration"
|
||||||
],
|
],
|
||||||
|
|
||||||
async update<D extends KubeObject>(resource: object | string): Promise<D> {
|
async update<K extends KubeObject>(resource: object | string): Promise<K | null> {
|
||||||
if (typeof resource === "string") {
|
if (typeof resource === "string") {
|
||||||
resource = jsYaml.safeLoad(resource);
|
resource = jsYaml.safeLoad(resource);
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ export const resourceApplierApi = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return items.length === 1 ? items[0] : items;
|
return items[0] as K ?? null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -28,7 +28,7 @@ import logger from "../../main/logger";
|
|||||||
import { apiManager } from "./api-manager";
|
import { apiManager } from "./api-manager";
|
||||||
import { apiKube } from "./index";
|
import { apiKube } from "./index";
|
||||||
import { createKubeApiURL, parseKubeApi } from "./kube-api-parse";
|
import { createKubeApiURL, parseKubeApi } from "./kube-api-parse";
|
||||||
import { IKubeObjectConstructor, KubeObject, KubeStatus } from "./kube-object";
|
import { KubeObjectConstructor, KubeObject, KubeStatus } from "./kube-object";
|
||||||
import byline from "byline";
|
import byline from "byline";
|
||||||
import type { IKubeWatchEvent } from "./kube-watch-api";
|
import type { IKubeWatchEvent } from "./kube-watch-api";
|
||||||
import { ReadableWebToNodeStream } from "../utils/readableStream";
|
import { ReadableWebToNodeStream } from "../utils/readableStream";
|
||||||
@ -49,7 +49,7 @@ export interface IKubeApiOptions<T extends KubeObject> {
|
|||||||
*/
|
*/
|
||||||
fallbackApiBases?: string[];
|
fallbackApiBases?: string[];
|
||||||
|
|
||||||
objectConstructor?: IKubeObjectConstructor<T>;
|
objectConstructor: KubeObjectConstructor<T>;
|
||||||
request?: KubeJsonApi;
|
request?: KubeJsonApi;
|
||||||
isNamespaced?: boolean;
|
isNamespaced?: boolean;
|
||||||
kind?: string;
|
kind?: string;
|
||||||
@ -99,7 +99,7 @@ export interface IKubeApiCluster {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function forCluster<T extends KubeObject>(cluster: IKubeApiCluster, kubeClass: IKubeObjectConstructor<T>): KubeApi<T> {
|
export function forCluster<T extends KubeObject>(cluster: IKubeApiCluster, kubeClass: KubeObjectConstructor<T>): KubeApi<T> {
|
||||||
const request = new KubeJsonApi({
|
const request = new KubeJsonApi({
|
||||||
apiBase: apiKubePrefix,
|
apiBase: apiKubePrefix,
|
||||||
debug: isDevelopment,
|
debug: isDevelopment,
|
||||||
@ -115,7 +115,7 @@ export function forCluster<T extends KubeObject>(cluster: IKubeApiCluster, kubeC
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ensureObjectSelfLink(api: KubeApi, object: KubeJsonApiData) {
|
export function ensureObjectSelfLink(api: KubeApi<KubeObject>, object: KubeJsonApiData) {
|
||||||
if (!object.metadata.selfLink) {
|
if (!object.metadata.selfLink) {
|
||||||
object.metadata.selfLink = createKubeApiURL({
|
object.metadata.selfLink = createKubeApiURL({
|
||||||
apiPrefix: api.apiPrefix,
|
apiPrefix: api.apiPrefix,
|
||||||
@ -127,7 +127,7 @@ export function ensureObjectSelfLink(api: KubeApi, object: KubeJsonApiData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type KubeApiWatchCallback = (data: IKubeWatchEvent, error: any) => void;
|
export type KubeApiWatchCallback = (data: IKubeWatchEvent<KubeJsonApiData>, error: any) => void;
|
||||||
|
|
||||||
export type KubeApiWatchOptions = {
|
export type KubeApiWatchOptions = {
|
||||||
namespace: string;
|
namespace: string;
|
||||||
@ -135,7 +135,7 @@ export type KubeApiWatchOptions = {
|
|||||||
abortController?: AbortController
|
abortController?: AbortController
|
||||||
};
|
};
|
||||||
|
|
||||||
export class KubeApi<T extends KubeObject = any> {
|
export class KubeApi<T extends KubeObject> {
|
||||||
readonly kind: string;
|
readonly kind: string;
|
||||||
readonly apiBase: string;
|
readonly apiBase: string;
|
||||||
readonly apiPrefix: string;
|
readonly apiPrefix: string;
|
||||||
@ -145,14 +145,14 @@ export class KubeApi<T extends KubeObject = any> {
|
|||||||
readonly apiResource: string;
|
readonly apiResource: string;
|
||||||
readonly isNamespaced: boolean;
|
readonly isNamespaced: boolean;
|
||||||
|
|
||||||
public objectConstructor: IKubeObjectConstructor<T>;
|
public objectConstructor: KubeObjectConstructor<T>;
|
||||||
protected request: KubeJsonApi;
|
protected request: KubeJsonApi;
|
||||||
protected resourceVersions = new Map<string, string>();
|
protected resourceVersions = new Map<string, string>();
|
||||||
protected watchDisposer: () => void;
|
protected watchDisposer: () => void;
|
||||||
|
|
||||||
constructor(protected options: IKubeApiOptions<T>) {
|
constructor(protected options: IKubeApiOptions<T>) {
|
||||||
const {
|
const {
|
||||||
objectConstructor = KubeObject as IKubeObjectConstructor,
|
objectConstructor,
|
||||||
request = apiKube,
|
request = apiKube,
|
||||||
kind = options.objectConstructor?.kind,
|
kind = options.objectConstructor?.kind,
|
||||||
isNamespaced = options.objectConstructor?.namespaced
|
isNamespaced = options.objectConstructor?.namespaced
|
||||||
@ -456,14 +456,14 @@ export class KubeApi<T extends KubeObject = any> {
|
|||||||
|
|
||||||
clearTimeout(timedRetry);
|
clearTimeout(timedRetry);
|
||||||
timedRetry = setTimeout(() => { // we did not get any kubernetes errors so let's retry
|
timedRetry = setTimeout(() => { // we did not get any kubernetes errors so let's retry
|
||||||
this.watch({...opts, namespace, callback});
|
this.watch({ ...opts, namespace, callback });
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
byline(nodeStream).on("data", (line) => {
|
byline(nodeStream).on("data", (line) => {
|
||||||
try {
|
try {
|
||||||
const event: IKubeWatchEvent = JSON.parse(line);
|
const event: IKubeWatchEvent<KubeJsonApiData> = JSON.parse(line);
|
||||||
|
|
||||||
if (event.type === "ERROR" && event.object.kind === "Status") {
|
if (event.type === "ERROR" && event.object.kind === "Status") {
|
||||||
errorReceived = true;
|
errorReceived = true;
|
||||||
@ -474,7 +474,7 @@ export class KubeApi<T extends KubeObject = any> {
|
|||||||
this.modifyWatchEvent(event);
|
this.modifyWatchEvent(event);
|
||||||
callback(event, null);
|
callback(event, null);
|
||||||
} catch (ignore) {
|
} catch (ignore) {
|
||||||
// ignore parse errors
|
// ignore parse errors
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@ -487,7 +487,7 @@ export class KubeApi<T extends KubeObject = any> {
|
|||||||
return abort;
|
return abort;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected modifyWatchEvent(event: IKubeWatchEvent) {
|
protected modifyWatchEvent(event: IKubeWatchEvent<KubeJsonApiData>) {
|
||||||
|
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case "ADDED":
|
case "ADDED":
|
||||||
|
|||||||
@ -31,13 +31,13 @@ import { resourceApplierApi } from "./endpoints/resource-applier.api";
|
|||||||
import { hasOptionalProperty, hasTypedProperty, isObject, isString, bindPredicate, isTypedArray, isRecord } from "../../common/utils/type-narrowing";
|
import { hasOptionalProperty, hasTypedProperty, isObject, isString, bindPredicate, isTypedArray, isRecord } from "../../common/utils/type-narrowing";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
export type IKubeObjectConstructor<T extends KubeObject = any> = (new (data: KubeJsonApiData | any) => T) & {
|
export type KubeObjectConstructor<K extends KubeObject> = (new (data: KubeJsonApiData | any) => K) & {
|
||||||
kind?: string;
|
kind?: string;
|
||||||
namespaced?: boolean;
|
namespaced?: boolean;
|
||||||
apiBase?: string;
|
apiBase?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IKubeObjectMetadata {
|
export interface KubeObjectMetadata {
|
||||||
uid: string;
|
uid: string;
|
||||||
name: string;
|
name: string;
|
||||||
namespace?: string;
|
namespace?: string;
|
||||||
@ -63,7 +63,7 @@ export interface IKubeObjectMetadata {
|
|||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IKubeStatus {
|
export interface KubeStatusData {
|
||||||
kind: string;
|
kind: string;
|
||||||
apiVersion: string;
|
apiVersion: string;
|
||||||
code: number;
|
code: number;
|
||||||
@ -78,7 +78,7 @@ export class KubeStatus {
|
|||||||
public readonly message: string;
|
public readonly message: string;
|
||||||
public readonly reason: string;
|
public readonly reason: string;
|
||||||
|
|
||||||
constructor(data: IKubeStatus) {
|
constructor(data: KubeStatusData) {
|
||||||
this.apiVersion = data.apiVersion;
|
this.apiVersion = data.apiVersion;
|
||||||
this.code = data.code;
|
this.code = data.code;
|
||||||
this.message = data.message || "";
|
this.message = data.message || "";
|
||||||
@ -86,9 +86,9 @@ export class KubeStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IKubeMetaField = keyof IKubeObjectMetadata;
|
export type KubeMetaField = keyof KubeObjectMetadata;
|
||||||
|
|
||||||
export class KubeObject<Metadata extends IKubeObjectMetadata = IKubeObjectMetadata, Status = any, Spec = any> implements ItemObject {
|
export class KubeObject<Metadata extends KubeObjectMetadata = KubeObjectMetadata, Status = any, Spec = any> implements ItemObject {
|
||||||
static readonly kind: string;
|
static readonly kind: string;
|
||||||
static readonly namespaced: boolean;
|
static readonly namespaced: boolean;
|
||||||
|
|
||||||
@ -277,14 +277,14 @@ export class KubeObject<Metadata extends IKubeObjectMetadata = IKubeObjectMetada
|
|||||||
}
|
}
|
||||||
|
|
||||||
// use unified resource-applier api for updating all k8s objects
|
// use unified resource-applier api for updating all k8s objects
|
||||||
async update<T extends KubeObject>(data: Partial<T>): Promise<T> {
|
async update<K extends KubeObject>(data: Partial<K>): Promise<K> {
|
||||||
for (const field of KubeObject.nonEditableFields) {
|
for (const field of KubeObject.nonEditableFields) {
|
||||||
if (!_.isEqual(_.get(this, field), _.get(data, field))) {
|
if (!_.isEqual(_.get(this, field), _.get(data, field))) {
|
||||||
throw new Error(`Failed to update Kube Object: ${field} has been modified`);
|
throw new Error(`Failed to update Kube Object: ${field} has been modified`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resourceApplierApi.update<T>({
|
return resourceApplierApi.update<K>({
|
||||||
...this.toPlainObject(),
|
...this.toPlainObject(),
|
||||||
...data,
|
...data,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -31,8 +31,9 @@ import { autoBind, Disposer, noop } from "../utils";
|
|||||||
import type { KubeApi } from "./kube-api";
|
import type { KubeApi } from "./kube-api";
|
||||||
import type { KubeJsonApiData } from "./kube-json-api";
|
import type { KubeJsonApiData } from "./kube-json-api";
|
||||||
import { isDebugging, isProduction } from "../../common/vars";
|
import { isDebugging, isProduction } from "../../common/vars";
|
||||||
|
import type { KubeObject } from "./kube-object";
|
||||||
|
|
||||||
export interface IKubeWatchEvent<T = KubeJsonApiData> {
|
export interface IKubeWatchEvent<T extends KubeJsonApiData> {
|
||||||
type: "ADDED" | "MODIFIED" | "DELETED" | "ERROR";
|
type: "ADDED" | "MODIFIED" | "DELETED" | "ERROR";
|
||||||
object?: T;
|
object?: T;
|
||||||
}
|
}
|
||||||
@ -58,11 +59,11 @@ export class KubeWatchApi {
|
|||||||
autoBind(this);
|
autoBind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
isAllowedApi(api: KubeApi): boolean {
|
isAllowedApi(api: KubeApi<KubeObject>): boolean {
|
||||||
return Boolean(this.context?.cluster.isAllowedResource(api.kind));
|
return Boolean(this.context?.cluster.isAllowedResource(api.kind));
|
||||||
}
|
}
|
||||||
|
|
||||||
preloadStores(stores: KubeObjectStore[], opts: { namespaces?: string[], loadOnce?: boolean } = {}) {
|
preloadStores(stores: KubeObjectStore<KubeObject>[], opts: { namespaces?: string[], loadOnce?: boolean } = {}) {
|
||||||
const limitRequests = plimit(1); // load stores one by one to allow quick skipping when fast clicking btw pages
|
const limitRequests = plimit(1); // load stores one by one to allow quick skipping when fast clicking btw pages
|
||||||
const preloading: Promise<any>[] = [];
|
const preloading: Promise<any>[] = [];
|
||||||
|
|
||||||
@ -80,7 +81,7 @@ export class KubeWatchApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribeStores(stores: KubeObjectStore[], opts: IKubeWatchSubscribeStoreOptions = {}): Disposer {
|
subscribeStores(stores: KubeObjectStore<KubeObject>[], opts: IKubeWatchSubscribeStoreOptions = {}): Disposer {
|
||||||
const { preload = true, waitUntilLoaded = true, loadOnce = false, } = opts;
|
const { preload = true, waitUntilLoaded = true, loadOnce = false, } = opts;
|
||||||
const subscribingNamespaces = opts.namespaces ?? this.context?.allNamespaces ?? [];
|
const subscribingNamespaces = opts.namespaces ?? this.context?.allNamespaces ?? [];
|
||||||
const unsubscribeList: Function[] = [];
|
const unsubscribeList: Function[] = [];
|
||||||
|
|||||||
@ -83,14 +83,14 @@ export class HelmCharts extends Component<Props> {
|
|||||||
store={helmChartStore}
|
store={helmChartStore}
|
||||||
isSelectable={false}
|
isSelectable={false}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (chart: HelmChart) => chart.getName(),
|
[columnId.name]: chart => chart.getName(),
|
||||||
[columnId.repo]: (chart: HelmChart) => chart.getRepository(),
|
[columnId.repo]: chart => chart.getRepository(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(chart: HelmChart) => chart.getName(),
|
chart => chart.getName(),
|
||||||
(chart: HelmChart) => chart.getVersion(),
|
chart => chart.getVersion(),
|
||||||
(chart: HelmChart) => chart.getAppVersion(),
|
chart => chart.getAppVersion(),
|
||||||
(chart: HelmChart) => chart.getKeywords(),
|
chart => chart.getKeywords(),
|
||||||
]}
|
]}
|
||||||
customizeHeader={({ searchProps }) => ({
|
customizeHeader={({ searchProps }) => ({
|
||||||
searchProps: {
|
searchProps: {
|
||||||
@ -106,7 +106,7 @@ export class HelmCharts extends Component<Props> {
|
|||||||
{ title: "App Version", className: "app-version", id: columnId.appVersion },
|
{ title: "App Version", className: "app-version", id: columnId.appVersion },
|
||||||
{ title: "Repository", className: "repository", sortBy: columnId.repo, id: columnId.repo },
|
{ title: "Repository", className: "repository", sortBy: columnId.repo, id: columnId.repo },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(chart: HelmChart) => [
|
renderTableContents={chart => [
|
||||||
<figure key="image">
|
<figure key="image">
|
||||||
<img
|
<img
|
||||||
src={chart.getIcon() || require("./helm-placeholder.svg")}
|
src={chart.getIcon() || require("./helm-placeholder.svg")}
|
||||||
|
|||||||
@ -111,19 +111,19 @@ export class HelmReleases extends Component<Props> {
|
|||||||
store={releaseStore}
|
store={releaseStore}
|
||||||
dependentStores={[secretsStore]}
|
dependentStores={[secretsStore]}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (release: HelmRelease) => release.getName(),
|
[columnId.name]: release => release.getName(),
|
||||||
[columnId.namespace]: (release: HelmRelease) => release.getNs(),
|
[columnId.namespace]: release => release.getNs(),
|
||||||
[columnId.revision]: (release: HelmRelease) => release.getRevision(),
|
[columnId.revision]: release => release.getRevision(),
|
||||||
[columnId.chart]: (release: HelmRelease) => release.getChart(),
|
[columnId.chart]: release => release.getChart(),
|
||||||
[columnId.status]: (release: HelmRelease) => release.getStatus(),
|
[columnId.status]: release => release.getStatus(),
|
||||||
[columnId.updated]: (release: HelmRelease) => release.getUpdated(false, false),
|
[columnId.updated]: release => release.getUpdated(false, false),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(release: HelmRelease) => release.getName(),
|
release => release.getName(),
|
||||||
(release: HelmRelease) => release.getNs(),
|
release => release.getNs(),
|
||||||
(release: HelmRelease) => release.getChart(),
|
release => release.getChart(),
|
||||||
(release: HelmRelease) => release.getStatus(),
|
release => release.getStatus(),
|
||||||
(release: HelmRelease) => release.getVersion(),
|
release => release.getVersion(),
|
||||||
]}
|
]}
|
||||||
customizeHeader={({ filters, searchProps, ...headerPlaceholders }) => ({
|
customizeHeader={({ filters, searchProps, ...headerPlaceholders }) => ({
|
||||||
filters: (
|
filters: (
|
||||||
@ -149,7 +149,7 @@ export class HelmReleases extends Component<Props> {
|
|||||||
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
||||||
{ title: "Updated", className: "updated", sortBy: columnId.updated, id: columnId.updated },
|
{ title: "Updated", className: "updated", sortBy: columnId.updated, id: columnId.updated },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(release: HelmRelease) => [
|
renderTableContents={release => [
|
||||||
release.getName(),
|
release.getName(),
|
||||||
release.getNs(),
|
release.getNs(),
|
||||||
release.getChart(),
|
release.getChart(),
|
||||||
@ -159,13 +159,13 @@ export class HelmReleases extends Component<Props> {
|
|||||||
{ title: release.getStatus(), className: kebabCase(release.getStatus()) },
|
{ title: release.getStatus(), className: kebabCase(release.getStatus()) },
|
||||||
release.getUpdated(),
|
release.getUpdated(),
|
||||||
]}
|
]}
|
||||||
renderItemMenu={(release: HelmRelease) => (
|
renderItemMenu={release => (
|
||||||
<HelmReleaseMenu
|
<HelmReleaseMenu
|
||||||
release={release}
|
release={release}
|
||||||
removeConfirmationMessage={this.renderRemoveDialogMessage([release])}
|
removeConfirmationMessage={this.renderRemoveDialogMessage([release])}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
customizeRemoveDialog={(selectedItems: HelmRelease[]) => ({
|
customizeRemoveDialog={selectedItems => ({
|
||||||
message: this.renderRemoveDialogMessage(selectedItems)
|
message: this.renderRemoveDialogMessage(selectedItems)
|
||||||
})}
|
})}
|
||||||
detailsItem={this.selectedRelease}
|
detailsItem={this.selectedRelease}
|
||||||
|
|||||||
@ -44,7 +44,6 @@ import { CatalogMenu } from "./catalog-menu";
|
|||||||
import { HotbarIcon } from "../hotbar/hotbar-icon";
|
import { HotbarIcon } from "../hotbar/hotbar-icon";
|
||||||
import { RenderDelay } from "../render-delay/render-delay";
|
import { RenderDelay } from "../render-delay/render-delay";
|
||||||
import { CatalogTopbar } from "../cluster-manager/catalog-topbar";
|
import { CatalogTopbar } from "../cluster-manager/catalog-topbar";
|
||||||
import type { TableSortCallback } from "../table";
|
|
||||||
|
|
||||||
export const previousActiveTab = createAppStorage("catalog-previous-active-tab", "");
|
export const previousActiveTab = createAppStorage("catalog-previous-active-tab", "");
|
||||||
|
|
||||||
@ -168,7 +167,7 @@ export class Catalog extends React.Component<Props> {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
<MenuItem key="add-to-hotbar" onClick={() => this.addToHotbar(item) }>
|
<MenuItem key="add-to-hotbar" onClick={() => this.addToHotbar(item)}>
|
||||||
Pin to Hotbar
|
Pin to Hotbar
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuActions>
|
</MenuActions>
|
||||||
@ -194,16 +193,6 @@ export class Catalog extends React.Component<Props> {
|
|||||||
renderList() {
|
renderList() {
|
||||||
const { activeCategory } = this.catalogEntityStore;
|
const { activeCategory } = this.catalogEntityStore;
|
||||||
const tableId = activeCategory ? `catalog-items-${activeCategory.metadata.name.replace(" ", "")}` : "catalog-items";
|
const tableId = activeCategory ? `catalog-items-${activeCategory.metadata.name.replace(" ", "")}` : "catalog-items";
|
||||||
let sortingCallbacks: { [sortBy: string]: TableSortCallback } = {
|
|
||||||
[sortBy.name]: (item: CatalogEntityItem<CatalogEntity>) => item.name,
|
|
||||||
[sortBy.source]: (item: CatalogEntityItem<CatalogEntity>) => item.source,
|
|
||||||
[sortBy.status]: (item: CatalogEntityItem<CatalogEntity>) => item.phase,
|
|
||||||
};
|
|
||||||
|
|
||||||
sortingCallbacks = activeCategory ? sortingCallbacks : {
|
|
||||||
...sortingCallbacks,
|
|
||||||
[sortBy.kind]: (item: CatalogEntityItem<CatalogEntity>) => item.kind,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.activeTab === undefined) {
|
if (this.activeTab === undefined) {
|
||||||
return null;
|
return null;
|
||||||
@ -217,9 +206,14 @@ export class Catalog extends React.Component<Props> {
|
|||||||
isConfigurable={true}
|
isConfigurable={true}
|
||||||
className="CatalogItemList"
|
className="CatalogItemList"
|
||||||
store={this.catalogEntityStore}
|
store={this.catalogEntityStore}
|
||||||
sortingCallbacks={sortingCallbacks}
|
sortingCallbacks={{
|
||||||
|
[sortBy.name]: item => item.name,
|
||||||
|
[sortBy.source]: item => item.source,
|
||||||
|
[sortBy.status]: item => item.phase,
|
||||||
|
[sortBy.kind]: item => item.kind,
|
||||||
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(entity: CatalogEntityItem<CatalogEntity>) => entity.searchFields,
|
entity => entity.searchFields,
|
||||||
]}
|
]}
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
{ title: "", className: css.iconCell, id: "icon" },
|
{ title: "", className: css.iconCell, id: "icon" },
|
||||||
@ -229,10 +223,10 @@ export class Catalog extends React.Component<Props> {
|
|||||||
{ title: "Labels", className: css.labelsCell, id: "labels" },
|
{ title: "Labels", className: css.labelsCell, id: "labels" },
|
||||||
{ title: "Status", className: css.statusCell, sortBy: sortBy.status, id: "status" },
|
{ title: "Status", className: css.statusCell, sortBy: sortBy.status, id: "status" },
|
||||||
].filter(Boolean)}
|
].filter(Boolean)}
|
||||||
customizeTableRowProps={(item: CatalogEntityItem<CatalogEntity>) => ({
|
customizeTableRowProps={item => ({
|
||||||
disabled: !item.enabled,
|
disabled: !item.enabled,
|
||||||
})}
|
})}
|
||||||
renderTableContents={(item: CatalogEntityItem<CatalogEntity>) => [
|
renderTableContents={item => [
|
||||||
this.renderIcon(item),
|
this.renderIcon(item),
|
||||||
item.name,
|
item.name,
|
||||||
!activeCategory && item.kind,
|
!activeCategory && item.kind,
|
||||||
|
|||||||
@ -64,15 +64,15 @@ export class HorizontalPodAutoscalers extends React.Component<Props> {
|
|||||||
tableId="configuration_hpa"
|
tableId="configuration_hpa"
|
||||||
className="HorizontalPodAutoscalers" store={hpaStore}
|
className="HorizontalPodAutoscalers" store={hpaStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (item: HorizontalPodAutoscaler) => item.getName(),
|
[columnId.name]: item => item.getName(),
|
||||||
[columnId.namespace]: (item: HorizontalPodAutoscaler) => item.getNs(),
|
[columnId.namespace]: item => item.getNs(),
|
||||||
[columnId.minPods]: (item: HorizontalPodAutoscaler) => item.getMinPods(),
|
[columnId.minPods]: item => item.getMinPods(),
|
||||||
[columnId.maxPods]: (item: HorizontalPodAutoscaler) => item.getMaxPods(),
|
[columnId.maxPods]: item => item.getMaxPods(),
|
||||||
[columnId.replicas]: (item: HorizontalPodAutoscaler) => item.getReplicas(),
|
[columnId.replicas]: item => item.getReplicas(),
|
||||||
[columnId.age]: (item: HorizontalPodAutoscaler) => item.getTimeDiffFromNow(),
|
[columnId.age]: item => item.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(item: HorizontalPodAutoscaler) => item.getSearchFields()
|
item => item.getSearchFields()
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Horizontal Pod Autoscalers"
|
renderHeaderTitle="Horizontal Pod Autoscalers"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -86,7 +86,7 @@ export class HorizontalPodAutoscalers extends React.Component<Props> {
|
|||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
{ title: "Status", className: "status", id: columnId.status },
|
{ title: "Status", className: "status", id: columnId.status },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(hpa: HorizontalPodAutoscaler) => [
|
renderTableContents={hpa => [
|
||||||
hpa.getName(),
|
hpa.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={hpa} />,
|
<KubeObjectStatusIcon key="icon" object={hpa} />,
|
||||||
hpa.getNs(),
|
hpa.getNs(),
|
||||||
|
|||||||
@ -27,7 +27,6 @@ import { KubeObjectListLayout } from "../kube-object/kube-object-list-layout";
|
|||||||
import { limitRangeStore } from "./limit-ranges.store";
|
import { limitRangeStore } from "./limit-ranges.store";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||||
import type { LimitRange } from "../../api/endpoints/limit-range.api";
|
|
||||||
import type { LimitRangeRouteParams } from "../../../common/routes";
|
import type { LimitRangeRouteParams } from "../../../common/routes";
|
||||||
|
|
||||||
enum columnId {
|
enum columnId {
|
||||||
@ -49,22 +48,22 @@ export class LimitRanges extends React.Component<Props> {
|
|||||||
className="LimitRanges"
|
className="LimitRanges"
|
||||||
store={limitRangeStore}
|
store={limitRangeStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (item: LimitRange) => item.getName(),
|
[columnId.name]: item => item.getName(),
|
||||||
[columnId.namespace]: (item: LimitRange) => item.getNs(),
|
[columnId.namespace]: item => item.getNs(),
|
||||||
[columnId.age]: (item: LimitRange) => item.getTimeDiffFromNow(),
|
[columnId.age]: item => item.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(item: LimitRange) => item.getName(),
|
item => item.getName(),
|
||||||
(item: LimitRange) => item.getNs(),
|
item => item.getNs(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle={"Limit Ranges"}
|
renderHeaderTitle="Limit Ranges"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
|
||||||
{ className: "warning", showWithColumn: columnId.name },
|
{ className: "warning", showWithColumn: columnId.name },
|
||||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(limitRange: LimitRange) => [
|
renderTableContents={limitRange => [
|
||||||
limitRange.getName(),
|
limitRange.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={limitRange}/>,
|
<KubeObjectStatusIcon key="icon" object={limitRange}/>,
|
||||||
limitRange.getNs(),
|
limitRange.getNs(),
|
||||||
|
|||||||
@ -25,7 +25,6 @@ import React from "react";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import type { RouteComponentProps } from "react-router";
|
import type { RouteComponentProps } from "react-router";
|
||||||
import { configMapsStore } from "./config-maps.store";
|
import { configMapsStore } from "./config-maps.store";
|
||||||
import type { ConfigMap } from "../../api/endpoints/configmap.api";
|
|
||||||
import { KubeObjectListLayout } from "../kube-object";
|
import { KubeObjectListLayout } from "../kube-object";
|
||||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||||
import type { ConfigMapsRouteParams } from "../../../common/routes";
|
import type { ConfigMapsRouteParams } from "../../../common/routes";
|
||||||
@ -49,14 +48,14 @@ export class ConfigMaps extends React.Component<Props> {
|
|||||||
tableId="configuration_configmaps"
|
tableId="configuration_configmaps"
|
||||||
className="ConfigMaps" store={configMapsStore}
|
className="ConfigMaps" store={configMapsStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (item: ConfigMap) => item.getName(),
|
[columnId.name]: item => item.getName(),
|
||||||
[columnId.namespace]: (item: ConfigMap) => item.getNs(),
|
[columnId.namespace]: item => item.getNs(),
|
||||||
[columnId.keys]: (item: ConfigMap) => item.getKeys(),
|
[columnId.keys]: item => item.getKeys(),
|
||||||
[columnId.age]: (item: ConfigMap) => item.getTimeDiffFromNow(),
|
[columnId.age]: item => item.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(item: ConfigMap) => item.getSearchFields(),
|
item => item.getSearchFields(),
|
||||||
(item: ConfigMap) => item.getKeys()
|
item => item.getKeys()
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Config Maps"
|
renderHeaderTitle="Config Maps"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -66,7 +65,7 @@ export class ConfigMaps extends React.Component<Props> {
|
|||||||
{ title: "Keys", className: "keys", sortBy: columnId.keys, id: columnId.keys },
|
{ title: "Keys", className: "keys", sortBy: columnId.keys, id: columnId.keys },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(configMap: ConfigMap) => [
|
renderTableContents={configMap => [
|
||||||
configMap.getName(),
|
configMap.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={configMap}/>,
|
<KubeObjectStatusIcon key="icon" object={configMap}/>,
|
||||||
configMap.getNs(),
|
configMap.getNs(),
|
||||||
|
|||||||
@ -51,16 +51,16 @@ export class PodDisruptionBudgets extends React.Component<Props> {
|
|||||||
className="PodDisruptionBudgets"
|
className="PodDisruptionBudgets"
|
||||||
store={podDisruptionBudgetsStore}
|
store={podDisruptionBudgetsStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (pdb: PodDisruptionBudget) => pdb.getName(),
|
[columnId.name]: pdb => pdb.getName(),
|
||||||
[columnId.namespace]: (pdb: PodDisruptionBudget) => pdb.getNs(),
|
[columnId.namespace]: pdb => pdb.getNs(),
|
||||||
[columnId.minAvailable]: (pdb: PodDisruptionBudget) => pdb.getMinAvailable(),
|
[columnId.minAvailable]: pdb => pdb.getMinAvailable(),
|
||||||
[columnId.maxUnavailable]: (pdb: PodDisruptionBudget) => pdb.getMaxUnavailable(),
|
[columnId.maxUnavailable]: pdb => pdb.getMaxUnavailable(),
|
||||||
[columnId.currentHealthy]: (pdb: PodDisruptionBudget) => pdb.getCurrentHealthy(),
|
[columnId.currentHealthy]: pdb => pdb.getCurrentHealthy(),
|
||||||
[columnId.desiredHealthy]: (pdb: PodDisruptionBudget) => pdb.getDesiredHealthy(),
|
[columnId.desiredHealthy]: pdb => pdb.getDesiredHealthy(),
|
||||||
[columnId.age]: (pdb: PodDisruptionBudget) => pdb.getAge(),
|
[columnId.age]: pdb => pdb.getAge(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(pdb: PodDisruptionBudget) => pdb.getSearchFields(),
|
pdb => pdb.getSearchFields(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Pod Disruption Budgets"
|
renderHeaderTitle="Pod Disruption Budgets"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -73,7 +73,7 @@ export class PodDisruptionBudgets extends React.Component<Props> {
|
|||||||
{ title: "Desired Healthy", className: "desired-healthy", sortBy: columnId.desiredHealthy, id: columnId.desiredHealthy },
|
{ title: "Desired Healthy", className: "desired-healthy", sortBy: columnId.desiredHealthy, id: columnId.desiredHealthy },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(pdb: PodDisruptionBudget) => {
|
renderTableContents={pdb => {
|
||||||
return [
|
return [
|
||||||
pdb.getName(),
|
pdb.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={pdb} />,
|
<KubeObjectStatusIcon key="icon" object={pdb} />,
|
||||||
|
|||||||
@ -25,7 +25,6 @@ import React from "react";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import type { RouteComponentProps } from "react-router";
|
import type { RouteComponentProps } from "react-router";
|
||||||
import { KubeObjectListLayout } from "../kube-object";
|
import { KubeObjectListLayout } from "../kube-object";
|
||||||
import type { ResourceQuota } from "../../api/endpoints/resource-quota.api";
|
|
||||||
import { AddQuotaDialog } from "./add-quota-dialog";
|
import { AddQuotaDialog } from "./add-quota-dialog";
|
||||||
import { resourceQuotaStore } from "./resource-quotas.store";
|
import { resourceQuotaStore } from "./resource-quotas.store";
|
||||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||||
@ -50,13 +49,13 @@ export class ResourceQuotas extends React.Component<Props> {
|
|||||||
tableId="configuration_quotas"
|
tableId="configuration_quotas"
|
||||||
className="ResourceQuotas" store={resourceQuotaStore}
|
className="ResourceQuotas" store={resourceQuotaStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (item: ResourceQuota) => item.getName(),
|
[columnId.name]: item => item.getName(),
|
||||||
[columnId.namespace]: (item: ResourceQuota) => item.getNs(),
|
[columnId.namespace]: item => item.getNs(),
|
||||||
[columnId.age]: (item: ResourceQuota) => item.getTimeDiffFromNow(),
|
[columnId.age]: item => item.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(item: ResourceQuota) => item.getSearchFields(),
|
item => item.getSearchFields(),
|
||||||
(item: ResourceQuota) => item.getName(),
|
item => item.getName(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Resource Quotas"
|
renderHeaderTitle="Resource Quotas"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -65,7 +64,7 @@ export class ResourceQuotas extends React.Component<Props> {
|
|||||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(resourceQuota: ResourceQuota) => [
|
renderTableContents={resourceQuota => [
|
||||||
resourceQuota.getName(),
|
resourceQuota.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={resourceQuota}/>,
|
<KubeObjectStatusIcon key="icon" object={resourceQuota}/>,
|
||||||
resourceQuota.getNs(),
|
resourceQuota.getNs(),
|
||||||
|
|||||||
@ -33,7 +33,7 @@ import { SubTitle } from "../layout/sub-title";
|
|||||||
import { NamespaceSelect } from "../+namespaces/namespace-select";
|
import { NamespaceSelect } from "../+namespaces/namespace-select";
|
||||||
import { Select, SelectOption } from "../select";
|
import { Select, SelectOption } from "../select";
|
||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import type { IKubeObjectMetadata } from "../../api/kube-object";
|
import type { KubeObjectMetadata } from "../../api/kube-object";
|
||||||
import { base64 } from "../../utils";
|
import { base64 } from "../../utils";
|
||||||
import { Notifications } from "../notifications";
|
import { Notifications } from "../notifications";
|
||||||
import upperFirst from "lodash/upperFirst";
|
import upperFirst from "lodash/upperFirst";
|
||||||
@ -127,7 +127,7 @@ export class AddSecretDialog extends React.Component<Props> {
|
|||||||
namespace,
|
namespace,
|
||||||
annotations: this.getDataFromFields(annotations),
|
annotations: this.getDataFromFields(annotations),
|
||||||
labels: this.getDataFromFields(labels),
|
labels: this.getDataFromFields(labels),
|
||||||
} as IKubeObjectMetadata
|
} as KubeObjectMetadata
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import "./secrets.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import type { RouteComponentProps } from "react-router";
|
import type { RouteComponentProps } from "react-router";
|
||||||
import type { Secret } from "../../api/endpoints";
|
|
||||||
import { AddSecretDialog } from "./add-secret-dialog";
|
import { AddSecretDialog } from "./add-secret-dialog";
|
||||||
import { KubeObjectListLayout } from "../kube-object";
|
import { KubeObjectListLayout } from "../kube-object";
|
||||||
import { Badge } from "../badge";
|
import { Badge } from "../badge";
|
||||||
@ -54,16 +53,16 @@ export class Secrets extends React.Component<Props> {
|
|||||||
tableId="configuration_secrets"
|
tableId="configuration_secrets"
|
||||||
className="Secrets" store={secretsStore}
|
className="Secrets" store={secretsStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (item: Secret) => item.getName(),
|
[columnId.name]: item => item.getName(),
|
||||||
[columnId.namespace]: (item: Secret) => item.getNs(),
|
[columnId.namespace]: item => item.getNs(),
|
||||||
[columnId.labels]: (item: Secret) => item.getLabels(),
|
[columnId.labels]: item => item.getLabels(),
|
||||||
[columnId.keys]: (item: Secret) => item.getKeys(),
|
[columnId.keys]: item => item.getKeys(),
|
||||||
[columnId.type]: (item: Secret) => item.type,
|
[columnId.type]: item => item.type,
|
||||||
[columnId.age]: (item: Secret) => item.getTimeDiffFromNow(),
|
[columnId.age]: item => item.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(item: Secret) => item.getSearchFields(),
|
item => item.getSearchFields(),
|
||||||
(item: Secret) => item.getKeys(),
|
item => item.getKeys(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Secrets"
|
renderHeaderTitle="Secrets"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -75,7 +74,7 @@ export class Secrets extends React.Component<Props> {
|
|||||||
{ title: "Type", className: "type", sortBy: columnId.type, id: columnId.type },
|
{ title: "Type", className: "type", sortBy: columnId.type, id: columnId.type },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(secret: Secret) => [
|
renderTableContents={secret => [
|
||||||
secret.getName(),
|
secret.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={secret} />,
|
<KubeObjectStatusIcon key="icon" object={secret} />,
|
||||||
secret.getNs(),
|
secret.getNs(),
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import type { CustomResourceDefinition } from "../../api/endpoints/crd.api";
|
|||||||
import { Select, SelectOption } from "../select";
|
import { Select, SelectOption } from "../select";
|
||||||
import { createPageParam } from "../../navigation";
|
import { createPageParam } from "../../navigation";
|
||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
|
import type { TableSortCallbacks } from "../table";
|
||||||
|
|
||||||
export const crdGroupsUrlParam = createPageParam<string[]>({
|
export const crdGroupsUrlParam = createPageParam<string[]>({
|
||||||
name: "groups",
|
name: "groups",
|
||||||
@ -78,11 +79,11 @@ export class CrdList extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { items, selectedGroups } = this;
|
const { items, selectedGroups } = this;
|
||||||
const sortingCallbacks = {
|
const sortingCallbacks: TableSortCallbacks<CustomResourceDefinition> = {
|
||||||
[columnId.kind]: (crd: CustomResourceDefinition) => crd.getResourceKind(),
|
[columnId.kind]: crd => crd.getResourceKind(),
|
||||||
[columnId.group]: (crd: CustomResourceDefinition) => crd.getGroup(),
|
[columnId.group]: crd => crd.getGroup(),
|
||||||
[columnId.version]: (crd: CustomResourceDefinition) => crd.getVersion(),
|
[columnId.version]: crd => crd.getVersion(),
|
||||||
[columnId.scope]: (crd: CustomResourceDefinition) => crd.getScope(),
|
[columnId.scope]: crd => crd.getScope(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -137,7 +138,7 @@ export class CrdList extends React.Component {
|
|||||||
{ title: "Scope", className: "scope", sortBy: columnId.scope, id: columnId.scope },
|
{ title: "Scope", className: "scope", sortBy: columnId.scope, id: columnId.scope },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(crd: CustomResourceDefinition) => [
|
renderTableContents={crd => [
|
||||||
<Link key="link" to={crd.getResourceUrl()} onClick={stopPropagation}>
|
<Link key="link" to={crd.getResourceUrl()} onClick={stopPropagation}>
|
||||||
{crd.getResourceKind()}
|
{crd.getResourceKind()}
|
||||||
</Link>,
|
</Link>,
|
||||||
|
|||||||
@ -23,10 +23,10 @@ import type { KubeApi } from "../../api/kube-api";
|
|||||||
import { KubeObjectStore } from "../../kube-object.store";
|
import { KubeObjectStore } from "../../kube-object.store";
|
||||||
import type { KubeObject } from "../../api/kube-object";
|
import type { KubeObject } from "../../api/kube-object";
|
||||||
|
|
||||||
export class CRDResourceStore<T extends KubeObject = any> extends KubeObjectStore<T> {
|
export class CRDResourceStore<K extends KubeObject> extends KubeObjectStore<K> {
|
||||||
api: KubeApi;
|
api: KubeApi<K>;
|
||||||
|
|
||||||
constructor(api: KubeApi<T>) {
|
constructor(api: KubeApi<K>) {
|
||||||
super();
|
super();
|
||||||
this.api = api;
|
this.api = api;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,7 +29,7 @@ import { KubeObjectListLayout } from "../kube-object";
|
|||||||
import type { KubeObject } from "../../api/kube-object";
|
import type { KubeObject } from "../../api/kube-object";
|
||||||
import { autorun, computed, makeObservable } from "mobx";
|
import { autorun, computed, makeObservable } from "mobx";
|
||||||
import { crdStore } from "./crd.store";
|
import { crdStore } from "./crd.store";
|
||||||
import type { TableSortCallback } from "../table";
|
import type { TableSortCallbacks } from "../table";
|
||||||
import { apiManager } from "../../api/api-manager";
|
import { apiManager } from "../../api/api-manager";
|
||||||
import { parseJsonPath } from "../../utils/jsonPath";
|
import { parseJsonPath } from "../../utils/jsonPath";
|
||||||
import type { CRDRouteParams } from "../../../common/routes";
|
import type { CRDRouteParams } from "../../../common/routes";
|
||||||
@ -80,14 +80,14 @@ export class CrdResources extends React.Component<Props> {
|
|||||||
if (!crd) return null;
|
if (!crd) return null;
|
||||||
const isNamespaced = crd.isNamespaced();
|
const isNamespaced = crd.isNamespaced();
|
||||||
const extraColumns = crd.getPrinterColumns(false); // Cols with priority bigger than 0 are shown in details
|
const extraColumns = crd.getPrinterColumns(false); // Cols with priority bigger than 0 are shown in details
|
||||||
const sortingCallbacks: { [sortBy: string]: TableSortCallback } = {
|
const sortingCallbacks: TableSortCallbacks<KubeObject> = {
|
||||||
[columnId.name]: (item: KubeObject) => item.getName(),
|
[columnId.name]: item => item.getName(),
|
||||||
[columnId.namespace]: (item: KubeObject) => item.getNs(),
|
[columnId.namespace]: item => item.getNs(),
|
||||||
[columnId.age]: (item: KubeObject) => item.getTimeDiffFromNow(),
|
[columnId.age]: item => item.getTimeDiffFromNow(),
|
||||||
};
|
};
|
||||||
|
|
||||||
extraColumns.forEach(column => {
|
extraColumns.forEach(column => {
|
||||||
sortingCallbacks[column.name] = (item: KubeObject) => jsonPath.value(item, parseJsonPath(column.jsonPath.slice(1)));
|
sortingCallbacks[column.name] = item => jsonPath.value(item, parseJsonPath(column.jsonPath.slice(1)));
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -98,7 +98,7 @@ export class CrdResources extends React.Component<Props> {
|
|||||||
store={store}
|
store={store}
|
||||||
sortingCallbacks={sortingCallbacks}
|
sortingCallbacks={sortingCallbacks}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(item: KubeObject) => item.getSearchFields(),
|
item => item.getSearchFields(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle={crd.getResourceTitle()}
|
renderHeaderTitle={crd.getResourceTitle()}
|
||||||
customizeHeader={({ searchProps, ...headerPlaceholders }) => ({
|
customizeHeader={({ searchProps, ...headerPlaceholders }) => ({
|
||||||
@ -123,7 +123,7 @@ export class CrdResources extends React.Component<Props> {
|
|||||||
}),
|
}),
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(crdInstance: KubeObject) => [
|
renderTableContents={crdInstance => [
|
||||||
crdInstance.getName(),
|
crdInstance.getName(),
|
||||||
isNamespaced && crdInstance.getNs(),
|
isNamespaced && crdInstance.getNs(),
|
||||||
...extraColumns.map((column) => {
|
...extraColumns.map((column) => {
|
||||||
|
|||||||
@ -26,13 +26,18 @@ import { crdApi, CustomResourceDefinition } from "../../api/endpoints/crd.api";
|
|||||||
import { apiManager } from "../../api/api-manager";
|
import { apiManager } from "../../api/api-manager";
|
||||||
import { KubeApi } from "../../api/kube-api";
|
import { KubeApi } from "../../api/kube-api";
|
||||||
import { CRDResourceStore } from "./crd-resource.store";
|
import { CRDResourceStore } from "./crd-resource.store";
|
||||||
import type { KubeObject } from "../../api/kube-object";
|
import { KubeObject } from "../../api/kube-object";
|
||||||
|
|
||||||
function initStore(crd: CustomResourceDefinition) {
|
function initStore(crd: CustomResourceDefinition) {
|
||||||
const apiBase = crd.getResourceApiBase();
|
const apiBase = crd.getResourceApiBase();
|
||||||
const kind = crd.getResourceKind();
|
const kind = crd.getResourceKind();
|
||||||
const isNamespaced = crd.isNamespaced();
|
const isNamespaced = crd.isNamespaced();
|
||||||
const api = apiManager.getApi(apiBase) || new KubeApi({ apiBase, kind, isNamespaced });
|
const api = apiManager.getApi(apiBase) ?? new KubeApi({
|
||||||
|
objectConstructor: KubeObject,
|
||||||
|
apiBase,
|
||||||
|
kind,
|
||||||
|
isNamespaced,
|
||||||
|
});
|
||||||
|
|
||||||
if (!apiManager.getStore(api)) {
|
if (!apiManager.getStore(api)) {
|
||||||
apiManager.registerStore(new CRDResourceStore(api));
|
apiManager.registerStore(new CRDResourceStore(api));
|
||||||
|
|||||||
@ -29,7 +29,7 @@ import { TabLayout } from "../layout/tab-layout";
|
|||||||
import { EventStore, eventStore } from "./event.store";
|
import { EventStore, eventStore } from "./event.store";
|
||||||
import { getDetailsUrl, KubeObjectListLayout, KubeObjectListLayoutProps } from "../kube-object";
|
import { getDetailsUrl, KubeObjectListLayout, KubeObjectListLayoutProps } from "../kube-object";
|
||||||
import type { KubeEvent } from "../../api/endpoints/events.api";
|
import type { KubeEvent } from "../../api/endpoints/events.api";
|
||||||
import type { TableSortCallbacks, TableSortParams, TableProps } from "../table";
|
import type { TableSortCallbacks, TableSortParams } from "../table";
|
||||||
import type { HeaderCustomizer } from "../item-object-list";
|
import type { HeaderCustomizer } from "../item-object-list";
|
||||||
import { Tooltip } from "../tooltip";
|
import { Tooltip } from "../tooltip";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
@ -49,7 +49,7 @@ enum columnId {
|
|||||||
lastSeen = "last-seen",
|
lastSeen = "last-seen",
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props extends Partial<KubeObjectListLayoutProps> {
|
interface Props extends Partial<KubeObjectListLayoutProps<KubeEvent>> {
|
||||||
className?: IClassName;
|
className?: IClassName;
|
||||||
compact?: boolean;
|
compact?: boolean;
|
||||||
compactLimit?: number;
|
compactLimit?: number;
|
||||||
@ -69,19 +69,13 @@ export class Events extends React.Component<Props> {
|
|||||||
orderBy: "asc",
|
orderBy: "asc",
|
||||||
};
|
};
|
||||||
|
|
||||||
private sortingCallbacks: TableSortCallbacks = {
|
private sortingCallbacks: TableSortCallbacks<KubeEvent> = {
|
||||||
[columnId.namespace]: (event: KubeEvent) => event.getNs(),
|
[columnId.namespace]: event => event.getNs(),
|
||||||
[columnId.type]: (event: KubeEvent) => event.type,
|
[columnId.type]: event => event.type,
|
||||||
[columnId.object]: (event: KubeEvent) => event.involvedObject.name,
|
[columnId.object]: event => event.involvedObject.name,
|
||||||
[columnId.count]: (event: KubeEvent) => event.count,
|
[columnId.count]: event => event.count,
|
||||||
[columnId.age]: (event: KubeEvent) => event.getTimeDiffFromNow(),
|
[columnId.age]: event => event.getTimeDiffFromNow(),
|
||||||
[columnId.lastSeen]: (event: KubeEvent) => this.now - new Date(event.lastTimestamp).getTime(),
|
[columnId.lastSeen]: event => this.now - new Date(event.lastTimestamp).getTime(),
|
||||||
};
|
|
||||||
|
|
||||||
private tableConfiguration: TableProps = {
|
|
||||||
sortSyncWithUrl: false,
|
|
||||||
sortByDefault: this.sorting,
|
|
||||||
onSort: params => this.sorting = params,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
@ -160,13 +154,17 @@ export class Events extends React.Component<Props> {
|
|||||||
isSelectable={false}
|
isSelectable={false}
|
||||||
items={visibleItems}
|
items={visibleItems}
|
||||||
virtual={!compact}
|
virtual={!compact}
|
||||||
tableProps={this.tableConfiguration}
|
tableProps={{
|
||||||
|
sortSyncWithUrl: false,
|
||||||
|
sortByDefault: this.sorting,
|
||||||
|
onSort: params => this.sorting = params,
|
||||||
|
}}
|
||||||
sortingCallbacks={this.sortingCallbacks}
|
sortingCallbacks={this.sortingCallbacks}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(event: KubeEvent) => event.getSearchFields(),
|
event => event.getSearchFields(),
|
||||||
(event: KubeEvent) => event.message,
|
event => event.message,
|
||||||
(event: KubeEvent) => event.getSource(),
|
event => event.getSource(),
|
||||||
(event: KubeEvent) => event.involvedObject.name,
|
event => event.involvedObject.name,
|
||||||
]}
|
]}
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
{ title: "Type", className: "type", sortBy: columnId.type, id: columnId.type },
|
{ title: "Type", className: "type", sortBy: columnId.type, id: columnId.type },
|
||||||
@ -178,7 +176,7 @@ export class Events extends React.Component<Props> {
|
|||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
{ title: "Last Seen", className: "last-seen", sortBy: columnId.lastSeen, id: columnId.lastSeen },
|
{ title: "Last Seen", className: "last-seen", sortBy: columnId.lastSeen, id: columnId.lastSeen },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(event: KubeEvent) => {
|
renderTableContents={event => {
|
||||||
const { involvedObject, type, message } = event;
|
const { involvedObject, type, message } = event;
|
||||||
const tooltipId = `message-${event.getId()}`;
|
const tooltipId = `message-${event.getId()}`;
|
||||||
const isWarning = event.isWarning();
|
const isWarning = event.isWarning();
|
||||||
|
|||||||
@ -109,7 +109,7 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
|||||||
return super.subscribe();
|
return super.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async loadItems(params: KubeObjectStoreLoadingParams) {
|
protected async loadItems(params: KubeObjectStoreLoadingParams<Namespace>): Promise<Namespace[]> {
|
||||||
const { allowedNamespaces } = this;
|
const { allowedNamespaces } = this;
|
||||||
|
|
||||||
let namespaces = await super.loadItems(params).catch(() => []);
|
let namespaces = await super.loadItems(params).catch(() => []);
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
import "./namespaces.scss";
|
import "./namespaces.scss";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Namespace, NamespaceStatus } from "../../api/endpoints";
|
import { NamespaceStatus } from "../../api/endpoints";
|
||||||
import { AddNamespaceDialog } from "./add-namespace-dialog";
|
import { AddNamespaceDialog } from "./add-namespace-dialog";
|
||||||
import { TabLayout } from "../layout/tab-layout";
|
import { TabLayout } from "../layout/tab-layout";
|
||||||
import { Badge } from "../badge";
|
import { Badge } from "../badge";
|
||||||
@ -51,14 +51,14 @@ export class Namespaces extends React.Component<Props> {
|
|||||||
tableId="namespaces"
|
tableId="namespaces"
|
||||||
className="Namespaces" store={namespaceStore}
|
className="Namespaces" store={namespaceStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (ns: Namespace) => ns.getName(),
|
[columnId.name]: ns => ns.getName(),
|
||||||
[columnId.labels]: (ns: Namespace) => ns.getLabels(),
|
[columnId.labels]: ns => ns.getLabels(),
|
||||||
[columnId.age]: (ns: Namespace) => ns.getTimeDiffFromNow(),
|
[columnId.age]: ns => ns.getTimeDiffFromNow(),
|
||||||
[columnId.status]: (ns: Namespace) => ns.getStatus(),
|
[columnId.status]: ns => ns.getStatus(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(item: Namespace) => item.getSearchFields(),
|
item => item.getSearchFields(),
|
||||||
(item: Namespace) => item.getStatus()
|
item => item.getStatus()
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Namespaces"
|
renderHeaderTitle="Namespaces"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -68,7 +68,7 @@ export class Namespaces extends React.Component<Props> {
|
|||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(item: Namespace) => [
|
renderTableContents={item => [
|
||||||
item.getName(),
|
item.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={item} />,
|
<KubeObjectStatusIcon key="icon" object={item} />,
|
||||||
item.getLabels().map(label => <Badge key={label} label={label}/>),
|
item.getLabels().map(label => <Badge key={label} label={label}/>),
|
||||||
@ -79,7 +79,7 @@ export class Namespaces extends React.Component<Props> {
|
|||||||
addTooltip: "Add Namespace",
|
addTooltip: "Add Namespace",
|
||||||
onAdd: () => AddNamespaceDialog.open(),
|
onAdd: () => AddNamespaceDialog.open(),
|
||||||
}}
|
}}
|
||||||
customizeTableRowProps={(item: Namespace) => ({
|
customizeTableRowProps={item => ({
|
||||||
disabled: item.getStatus() === NamespaceStatus.TERMINATING,
|
disabled: item.getStatus() === NamespaceStatus.TERMINATING,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import "./endpoints.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import type { RouteComponentProps } from "react-router-dom";
|
import type { RouteComponentProps } from "react-router-dom";
|
||||||
import type { Endpoint } from "../../api/endpoints/endpoint.api";
|
|
||||||
import { endpointStore } from "./endpoints.store";
|
import { endpointStore } from "./endpoints.store";
|
||||||
import { KubeObjectListLayout } from "../kube-object";
|
import { KubeObjectListLayout } from "../kube-object";
|
||||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||||
@ -49,12 +48,12 @@ export class Endpoints extends React.Component<Props> {
|
|||||||
tableId="network_endpoints"
|
tableId="network_endpoints"
|
||||||
className="Endpoints" store={endpointStore}
|
className="Endpoints" store={endpointStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (endpoint: Endpoint) => endpoint.getName(),
|
[columnId.name]: endpoint => endpoint.getName(),
|
||||||
[columnId.namespace]: (endpoint: Endpoint) => endpoint.getNs(),
|
[columnId.namespace]: endpoint => endpoint.getNs(),
|
||||||
[columnId.age]: (endpoint: Endpoint) => endpoint.getTimeDiffFromNow(),
|
[columnId.age]: endpoint => endpoint.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(endpoint: Endpoint) => endpoint.getSearchFields()
|
endpoint => endpoint.getSearchFields()
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Endpoints"
|
renderHeaderTitle="Endpoints"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -64,7 +63,7 @@ export class Endpoints extends React.Component<Props> {
|
|||||||
{ title: "Endpoints", className: "endpoints", id: columnId.endpoints },
|
{ title: "Endpoints", className: "endpoints", id: columnId.endpoints },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(endpoint: Endpoint) => [
|
renderTableContents={endpoint => [
|
||||||
endpoint.getName(),
|
endpoint.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={endpoint} />,
|
<KubeObjectStatusIcon key="icon" object={endpoint} />,
|
||||||
endpoint.getNs(),
|
endpoint.getNs(),
|
||||||
@ -72,7 +71,7 @@ export class Endpoints extends React.Component<Props> {
|
|||||||
endpoint.getAge(),
|
endpoint.getAge(),
|
||||||
]}
|
]}
|
||||||
tableProps={{
|
tableProps={{
|
||||||
customRowHeights: (item: Endpoint, lineHeight, paddings) => {
|
customRowHeights: (item, lineHeight, paddings) => {
|
||||||
const lines = item.getEndpointSubsets().length || 1;
|
const lines = item.getEndpointSubsets().length || 1;
|
||||||
|
|
||||||
return lines * lineHeight + paddings;
|
return lines * lineHeight + paddings;
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import "./ingresses.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import type { RouteComponentProps } from "react-router-dom";
|
import type { RouteComponentProps } from "react-router-dom";
|
||||||
import type { Ingress } from "../../api/endpoints/ingress.api";
|
|
||||||
import { ingressStore } from "./ingress.store";
|
import { ingressStore } from "./ingress.store";
|
||||||
import { KubeObjectListLayout } from "../kube-object";
|
import { KubeObjectListLayout } from "../kube-object";
|
||||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||||
@ -50,13 +49,13 @@ export class Ingresses extends React.Component<Props> {
|
|||||||
tableId="network_ingresses"
|
tableId="network_ingresses"
|
||||||
className="Ingresses" store={ingressStore}
|
className="Ingresses" store={ingressStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (ingress: Ingress) => ingress.getName(),
|
[columnId.name]: ingress => ingress.getName(),
|
||||||
[columnId.namespace]: (ingress: Ingress) => ingress.getNs(),
|
[columnId.namespace]: ingress => ingress.getNs(),
|
||||||
[columnId.age]: (ingress: Ingress) => ingress.getTimeDiffFromNow(),
|
[columnId.age]: ingress => ingress.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(ingress: Ingress) => ingress.getSearchFields(),
|
ingress => ingress.getSearchFields(),
|
||||||
(ingress: Ingress) => ingress.getPorts(),
|
ingress => ingress.getPorts(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Ingresses"
|
renderHeaderTitle="Ingresses"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -67,7 +66,7 @@ export class Ingresses extends React.Component<Props> {
|
|||||||
{ title: "Rules", className: "rules", id: columnId.rules },
|
{ title: "Rules", className: "rules", id: columnId.rules },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(ingress: Ingress) => [
|
renderTableContents={ingress => [
|
||||||
ingress.getName(),
|
ingress.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={ingress} />,
|
<KubeObjectStatusIcon key="icon" object={ingress} />,
|
||||||
ingress.getNs(),
|
ingress.getNs(),
|
||||||
@ -76,7 +75,7 @@ export class Ingresses extends React.Component<Props> {
|
|||||||
ingress.getAge(),
|
ingress.getAge(),
|
||||||
]}
|
]}
|
||||||
tableProps={{
|
tableProps={{
|
||||||
customRowHeights: (item: Ingress, lineHeight, paddings) => {
|
customRowHeights: (item, lineHeight, paddings) => {
|
||||||
const lines = item.getRoutes().length || 1;
|
const lines = item.getRoutes().length || 1;
|
||||||
|
|
||||||
return lines * lineHeight + paddings;
|
return lines * lineHeight + paddings;
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import "./network-policies.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import type { RouteComponentProps } from "react-router-dom";
|
import type { RouteComponentProps } from "react-router-dom";
|
||||||
import type { NetworkPolicy } from "../../api/endpoints/network-policy.api";
|
|
||||||
import { KubeObjectListLayout } from "../kube-object";
|
import { KubeObjectListLayout } from "../kube-object";
|
||||||
import { networkPolicyStore } from "./network-policy.store";
|
import { networkPolicyStore } from "./network-policy.store";
|
||||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||||
@ -49,12 +48,12 @@ export class NetworkPolicies extends React.Component<Props> {
|
|||||||
tableId="network_policies"
|
tableId="network_policies"
|
||||||
className="NetworkPolicies" store={networkPolicyStore}
|
className="NetworkPolicies" store={networkPolicyStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (item: NetworkPolicy) => item.getName(),
|
[columnId.name]: item => item.getName(),
|
||||||
[columnId.namespace]: (item: NetworkPolicy) => item.getNs(),
|
[columnId.namespace]: item => item.getNs(),
|
||||||
[columnId.age]: (item: NetworkPolicy) => item.getTimeDiffFromNow(),
|
[columnId.age]: item => item.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(item: NetworkPolicy) => item.getSearchFields(),
|
item => item.getSearchFields(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Network Policies"
|
renderHeaderTitle="Network Policies"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -64,7 +63,7 @@ export class NetworkPolicies extends React.Component<Props> {
|
|||||||
{ title: "Policy Types", className: "type", id: columnId.types },
|
{ title: "Policy Types", className: "type", id: columnId.types },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(item: NetworkPolicy) => [
|
renderTableContents={item => [
|
||||||
item.getName(),
|
item.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={item} />,
|
<KubeObjectStatusIcon key="icon" object={item} />,
|
||||||
item.getNs(),
|
item.getNs(),
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import "./services.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import type { RouteComponentProps } from "react-router";
|
import type { RouteComponentProps } from "react-router";
|
||||||
import type { Service } from "../../api/endpoints/service.api";
|
|
||||||
import { KubeObjectListLayout } from "../kube-object";
|
import { KubeObjectListLayout } from "../kube-object";
|
||||||
import { Badge } from "../badge";
|
import { Badge } from "../badge";
|
||||||
import { serviceStore } from "./services.store";
|
import { serviceStore } from "./services.store";
|
||||||
@ -55,19 +54,19 @@ export class Services extends React.Component<Props> {
|
|||||||
tableId="network_services"
|
tableId="network_services"
|
||||||
className="Services" store={serviceStore}
|
className="Services" store={serviceStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (service: Service) => service.getName(),
|
[columnId.name]: service => service.getName(),
|
||||||
[columnId.namespace]: (service: Service) => service.getNs(),
|
[columnId.namespace]: service => service.getNs(),
|
||||||
[columnId.selector]: (service: Service) => service.getSelector(),
|
[columnId.selector]: service => service.getSelector(),
|
||||||
[columnId.ports]: (service: Service) => (service.spec.ports || []).map(({ port }) => port)[0],
|
[columnId.ports]: service => (service.spec.ports || []).map(({ port }) => port)[0],
|
||||||
[columnId.clusterIp]: (service: Service) => service.getClusterIp(),
|
[columnId.clusterIp]: service => service.getClusterIp(),
|
||||||
[columnId.type]: (service: Service) => service.getType(),
|
[columnId.type]: service => service.getType(),
|
||||||
[columnId.age]: (service: Service) => service.getTimeDiffFromNow(),
|
[columnId.age]: service => service.getTimeDiffFromNow(),
|
||||||
[columnId.status]: (service: Service) => service.getStatus(),
|
[columnId.status]: service => service.getStatus(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(service: Service) => service.getSearchFields(),
|
service => service.getSearchFields(),
|
||||||
(service: Service) => service.getSelector().join(" "),
|
service => service.getSelector().join(" "),
|
||||||
(service: Service) => service.getPorts().join(" "),
|
service => service.getPorts().join(" "),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Services"
|
renderHeaderTitle="Services"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -82,7 +81,7 @@ export class Services extends React.Component<Props> {
|
|||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(service: Service) => [
|
renderTableContents={service => [
|
||||||
service.getName(),
|
service.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={service} />,
|
<KubeObjectStatusIcon key="icon" object={service} />,
|
||||||
service.getNs(),
|
service.getNs(),
|
||||||
|
|||||||
@ -198,21 +198,21 @@ export class Nodes extends React.Component<Props> {
|
|||||||
dependentStores={[podsStore]}
|
dependentStores={[podsStore]}
|
||||||
isSelectable={false}
|
isSelectable={false}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (node: Node) => node.getName(),
|
[columnId.name]: node => node.getName(),
|
||||||
[columnId.cpu]: (node: Node) => this.getLastMetricValues(node, ["cpuUsage"]),
|
[columnId.cpu]: node => this.getLastMetricValues(node, ["cpuUsage"]),
|
||||||
[columnId.memory]: (node: Node) => this.getLastMetricValues(node, ["memoryUsage"]),
|
[columnId.memory]: node => this.getLastMetricValues(node, ["memoryUsage"]),
|
||||||
[columnId.disk]: (node: Node) => this.getLastMetricValues(node, ["fsUsage"]),
|
[columnId.disk]: node => this.getLastMetricValues(node, ["fsUsage"]),
|
||||||
[columnId.conditions]: (node: Node) => node.getNodeConditionText(),
|
[columnId.conditions]: node => node.getNodeConditionText(),
|
||||||
[columnId.taints]: (node: Node) => node.getTaints().length,
|
[columnId.taints]: node => node.getTaints().length,
|
||||||
[columnId.roles]: (node: Node) => node.getRoleLabels(),
|
[columnId.roles]: node => node.getRoleLabels(),
|
||||||
[columnId.age]: (node: Node) => node.getTimeDiffFromNow(),
|
[columnId.age]: node => node.getTimeDiffFromNow(),
|
||||||
[columnId.version]: (node: Node) => node.getKubeletVersion(),
|
[columnId.version]: node => node.getKubeletVersion(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(node: Node) => node.getSearchFields(),
|
node => node.getSearchFields(),
|
||||||
(node: Node) => node.getRoleLabels(),
|
node => node.getRoleLabels(),
|
||||||
(node: Node) => node.getKubeletVersion(),
|
node => node.getKubeletVersion(),
|
||||||
(node: Node) => node.getNodeConditionText(),
|
node => node.getNodeConditionText(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Nodes"
|
renderHeaderTitle="Nodes"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -227,7 +227,7 @@ export class Nodes extends React.Component<Props> {
|
|||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
{ title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions },
|
{ title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(node: Node) => {
|
renderTableContents={node => {
|
||||||
const tooltipId = `node-taints-${node.getId()}`;
|
const tooltipId = `node-taints-${node.getId()}`;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|||||||
@ -25,7 +25,6 @@ import React from "react";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { KubeObjectListLayout } from "../kube-object";
|
import { KubeObjectListLayout } from "../kube-object";
|
||||||
import { podSecurityPoliciesStore } from "./pod-security-policies.store";
|
import { podSecurityPoliciesStore } from "./pod-security-policies.store";
|
||||||
import type { PodSecurityPolicy } from "../../api/endpoints";
|
|
||||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||||
|
|
||||||
enum columnId {
|
enum columnId {
|
||||||
@ -45,15 +44,15 @@ export class PodSecurityPolicies extends React.Component {
|
|||||||
className="PodSecurityPolicies"
|
className="PodSecurityPolicies"
|
||||||
store={podSecurityPoliciesStore}
|
store={podSecurityPoliciesStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (item: PodSecurityPolicy) => item.getName(),
|
[columnId.name]: item => item.getName(),
|
||||||
[columnId.volumes]: (item: PodSecurityPolicy) => item.getVolumes(),
|
[columnId.volumes]: item => item.getVolumes(),
|
||||||
[columnId.privileged]: (item: PodSecurityPolicy) => +item.isPrivileged(),
|
[columnId.privileged]: item => +item.isPrivileged(),
|
||||||
[columnId.age]: (item: PodSecurityPolicy) => item.getTimeDiffFromNow(),
|
[columnId.age]: item => item.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(item: PodSecurityPolicy) => item.getSearchFields(),
|
item => item.getSearchFields(),
|
||||||
(item: PodSecurityPolicy) => item.getVolumes(),
|
item => item.getVolumes(),
|
||||||
(item: PodSecurityPolicy) => Object.values(item.getRules()),
|
item => Object.values(item.getRules()),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Pod Security Policies"
|
renderHeaderTitle="Pod Security Policies"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -63,7 +62,7 @@ export class PodSecurityPolicies extends React.Component {
|
|||||||
{ title: "Volumes", className: "volumes", sortBy: columnId.volumes, id: columnId.volumes },
|
{ title: "Volumes", className: "volumes", sortBy: columnId.volumes, id: columnId.volumes },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(item: PodSecurityPolicy) => {
|
renderTableContents={item => {
|
||||||
return [
|
return [
|
||||||
item.getName(),
|
item.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={item} />,
|
<KubeObjectStatusIcon key="icon" object={item} />,
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import "./storage-classes.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import type { RouteComponentProps } from "react-router-dom";
|
import type { RouteComponentProps } from "react-router-dom";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import type { StorageClass } from "../../api/endpoints/storage-class.api";
|
|
||||||
import { KubeObjectListLayout } from "../kube-object";
|
import { KubeObjectListLayout } from "../kube-object";
|
||||||
import { storageClassStore } from "./storage-class.store";
|
import { storageClassStore } from "./storage-class.store";
|
||||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||||
@ -51,14 +50,14 @@ export class StorageClasses extends React.Component<Props> {
|
|||||||
className="StorageClasses"
|
className="StorageClasses"
|
||||||
store={storageClassStore}
|
store={storageClassStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (item: StorageClass) => item.getName(),
|
[columnId.name]: item => item.getName(),
|
||||||
[columnId.age]: (item: StorageClass) => item.getTimeDiffFromNow(),
|
[columnId.age]: item => item.getTimeDiffFromNow(),
|
||||||
[columnId.provisioner]: (item: StorageClass) => item.provisioner,
|
[columnId.provisioner]: item => item.provisioner,
|
||||||
[columnId.reclaimPolicy]: (item: StorageClass) => item.reclaimPolicy,
|
[columnId.reclaimPolicy]: item => item.reclaimPolicy,
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(item: StorageClass) => item.getSearchFields(),
|
item => item.getSearchFields(),
|
||||||
(item: StorageClass) => item.provisioner,
|
item => item.provisioner,
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Storage Classes"
|
renderHeaderTitle="Storage Classes"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -69,7 +68,7 @@ export class StorageClasses extends React.Component<Props> {
|
|||||||
{ title: "Default", className: "is-default", id: columnId.default },
|
{ title: "Default", className: "is-default", id: columnId.default },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(storageClass: StorageClass) => [
|
renderTableContents={storageClass => [
|
||||||
storageClass.getName(),
|
storageClass.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={storageClass} />,
|
<KubeObjectStatusIcon key="icon" object={storageClass} />,
|
||||||
storageClass.provisioner,
|
storageClass.provisioner,
|
||||||
|
|||||||
@ -25,7 +25,6 @@ import React from "react";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { Link, RouteComponentProps } from "react-router-dom";
|
import { Link, RouteComponentProps } from "react-router-dom";
|
||||||
import { volumeClaimStore } from "./volume-claim.store";
|
import { volumeClaimStore } from "./volume-claim.store";
|
||||||
import type { PersistentVolumeClaim } from "../../api/endpoints/persistent-volume-claims.api";
|
|
||||||
import { podsStore } from "../+workloads-pods/pods.store";
|
import { podsStore } from "../+workloads-pods/pods.store";
|
||||||
import { getDetailsUrl, KubeObjectListLayout } from "../kube-object";
|
import { getDetailsUrl, KubeObjectListLayout } from "../kube-object";
|
||||||
import { unitsToBytes } from "../../utils/convertMemory";
|
import { unitsToBytes } from "../../utils/convertMemory";
|
||||||
@ -58,17 +57,17 @@ export class PersistentVolumeClaims extends React.Component<Props> {
|
|||||||
store={volumeClaimStore}
|
store={volumeClaimStore}
|
||||||
dependentStores={[podsStore]}
|
dependentStores={[podsStore]}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (pvc: PersistentVolumeClaim) => pvc.getName(),
|
[columnId.name]: pvc => pvc.getName(),
|
||||||
[columnId.namespace]: (pvc: PersistentVolumeClaim) => pvc.getNs(),
|
[columnId.namespace]: pvc => pvc.getNs(),
|
||||||
[columnId.pods]: (pvc: PersistentVolumeClaim) => pvc.getPods(podsStore.items).map(pod => pod.getName()),
|
[columnId.pods]: pvc => pvc.getPods(podsStore.items).map(pod => pod.getName()),
|
||||||
[columnId.status]: (pvc: PersistentVolumeClaim) => pvc.getStatus(),
|
[columnId.status]: pvc => pvc.getStatus(),
|
||||||
[columnId.size]: (pvc: PersistentVolumeClaim) => unitsToBytes(pvc.getStorage()),
|
[columnId.size]: pvc => unitsToBytes(pvc.getStorage()),
|
||||||
[columnId.storageClass]: (pvc: PersistentVolumeClaim) => pvc.spec.storageClassName,
|
[columnId.storageClass]: pvc => pvc.spec.storageClassName,
|
||||||
[columnId.age]: (pvc: PersistentVolumeClaim) => pvc.getTimeDiffFromNow(),
|
[columnId.age]: pvc => pvc.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(item: PersistentVolumeClaim) => item.getSearchFields(),
|
item => item.getSearchFields(),
|
||||||
(item: PersistentVolumeClaim) => item.getPods(podsStore.items).map(pod => pod.getName()),
|
item => item.getPods(podsStore.items).map(pod => pod.getName()),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Persistent Volume Claims"
|
renderHeaderTitle="Persistent Volume Claims"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -81,7 +80,7 @@ export class PersistentVolumeClaims extends React.Component<Props> {
|
|||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(pvc: PersistentVolumeClaim) => {
|
renderTableContents={pvc => {
|
||||||
const pods = pvc.getPods(podsStore.items);
|
const pods = pvc.getPods(podsStore.items);
|
||||||
const { storageClassName } = pvc.spec;
|
const { storageClassName } = pvc.spec;
|
||||||
const storageClassDetailsUrl = getDetailsUrl(storageClassApi.getUrl({
|
const storageClassDetailsUrl = getDetailsUrl(storageClassApi.getUrl({
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import "./volumes.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { Link, RouteComponentProps } from "react-router-dom";
|
import { Link, RouteComponentProps } from "react-router-dom";
|
||||||
import type { PersistentVolume } from "../../api/endpoints/persistent-volume.api";
|
|
||||||
import { getDetailsUrl, KubeObjectListLayout } from "../kube-object";
|
import { getDetailsUrl, KubeObjectListLayout } from "../kube-object";
|
||||||
import { stopPropagation } from "../../utils";
|
import { stopPropagation } from "../../utils";
|
||||||
import { volumesStore } from "./volumes.store";
|
import { volumesStore } from "./volumes.store";
|
||||||
@ -54,15 +53,15 @@ export class PersistentVolumes extends React.Component<Props> {
|
|||||||
className="PersistentVolumes"
|
className="PersistentVolumes"
|
||||||
store={volumesStore}
|
store={volumesStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (item: PersistentVolume) => item.getName(),
|
[columnId.name]: item => item.getName(),
|
||||||
[columnId.storageClass]: (item: PersistentVolume) => item.getStorageClass(),
|
[columnId.storageClass]: item => item.getStorageClass(),
|
||||||
[columnId.capacity]: (item: PersistentVolume) => item.getCapacity(true),
|
[columnId.capacity]: item => item.getCapacity(true),
|
||||||
[columnId.status]: (item: PersistentVolume) => item.getStatus(),
|
[columnId.status]: item => item.getStatus(),
|
||||||
[columnId.age]: (item: PersistentVolume) => item.getTimeDiffFromNow(),
|
[columnId.age]: item => item.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(item: PersistentVolume) => item.getSearchFields(),
|
item => item.getSearchFields(),
|
||||||
(item: PersistentVolume) => item.getClaimRefName(),
|
item => item.getClaimRefName(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Persistent Volumes"
|
renderHeaderTitle="Persistent Volumes"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -74,7 +73,7 @@ export class PersistentVolumes extends React.Component<Props> {
|
|||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(volume: PersistentVolume) => {
|
renderTableContents={volume => {
|
||||||
const { claimRef, storageClassName } = volume.spec;
|
const { claimRef, storageClassName } = volume.spec;
|
||||||
const storageClassDetailsUrl = getDetailsUrl(storageClassApi.getUrl({
|
const storageClassDetailsUrl = getDetailsUrl(storageClassApi.getUrl({
|
||||||
name: storageClassName
|
name: storageClassName
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import "./view.scss";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { RouteComponentProps } from "react-router";
|
import type { RouteComponentProps } from "react-router";
|
||||||
import type { ClusterRoleBinding } from "../../../api/endpoints";
|
|
||||||
import { KubeObjectListLayout } from "../../kube-object";
|
import { KubeObjectListLayout } from "../../kube-object";
|
||||||
import { KubeObjectStatusIcon } from "../../kube-object-status-icon";
|
import { KubeObjectStatusIcon } from "../../kube-object-status-icon";
|
||||||
import { ClusterRoleBindingDialog } from "./dialog";
|
import { ClusterRoleBindingDialog } from "./dialog";
|
||||||
@ -55,13 +54,13 @@ export class ClusterRoleBindings extends React.Component<Props> {
|
|||||||
store={clusterRoleBindingsStore}
|
store={clusterRoleBindingsStore}
|
||||||
dependentStores={[clusterRolesStore, serviceAccountsStore]}
|
dependentStores={[clusterRolesStore, serviceAccountsStore]}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (binding: ClusterRoleBinding) => binding.getName(),
|
[columnId.name]: binding => binding.getName(),
|
||||||
[columnId.bindings]: (binding: ClusterRoleBinding) => binding.getSubjectNames(),
|
[columnId.bindings]: binding => binding.getSubjectNames(),
|
||||||
[columnId.age]: (binding: ClusterRoleBinding) => binding.getTimeDiffFromNow(),
|
[columnId.age]: binding => binding.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(binding: ClusterRoleBinding) => binding.getSearchFields(),
|
binding => binding.getSearchFields(),
|
||||||
(binding: ClusterRoleBinding) => binding.getSubjectNames(),
|
binding => binding.getSubjectNames(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Cluster Role Bindings"
|
renderHeaderTitle="Cluster Role Bindings"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -70,7 +69,7 @@ export class ClusterRoleBindings extends React.Component<Props> {
|
|||||||
{ title: "Bindings", className: "bindings", sortBy: columnId.bindings, id: columnId.bindings },
|
{ title: "Bindings", className: "bindings", sortBy: columnId.bindings, id: columnId.bindings },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(binding: ClusterRoleBinding) => [
|
renderTableContents={binding => [
|
||||||
binding.getName(),
|
binding.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={binding} />,
|
<KubeObjectStatusIcon key="icon" object={binding} />,
|
||||||
binding.getSubjectNames(),
|
binding.getSubjectNames(),
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import "./view.scss";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { RouteComponentProps } from "react-router";
|
import type { RouteComponentProps } from "react-router";
|
||||||
import type { ClusterRole } from "../../../api/endpoints";
|
|
||||||
import { KubeObjectListLayout } from "../../kube-object";
|
import { KubeObjectListLayout } from "../../kube-object";
|
||||||
import { KubeObjectStatusIcon } from "../../kube-object-status-icon";
|
import { KubeObjectStatusIcon } from "../../kube-object-status-icon";
|
||||||
import { AddClusterRoleDialog } from "./add-dialog";
|
import { AddClusterRoleDialog } from "./add-dialog";
|
||||||
@ -51,11 +50,11 @@ export class ClusterRoles extends React.Component<Props> {
|
|||||||
className="ClusterRoles"
|
className="ClusterRoles"
|
||||||
store={clusterRolesStore}
|
store={clusterRolesStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (clusterRole: ClusterRole) => clusterRole.getName(),
|
[columnId.name]: clusterRole => clusterRole.getName(),
|
||||||
[columnId.age]: (clusterRole: ClusterRole) => clusterRole.getTimeDiffFromNow(),
|
[columnId.age]: clusterRole => clusterRole.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(clusterRole: ClusterRole) => clusterRole.getSearchFields(),
|
clusterRole => clusterRole.getSearchFields(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Cluster Roles"
|
renderHeaderTitle="Cluster Roles"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -63,7 +62,7 @@ export class ClusterRoles extends React.Component<Props> {
|
|||||||
{ className: "warning", showWithColumn: columnId.name },
|
{ className: "warning", showWithColumn: columnId.name },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(clusterRole: ClusterRole) => [
|
renderTableContents={clusterRole => [
|
||||||
clusterRole.getName(),
|
clusterRole.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={clusterRole} />,
|
<KubeObjectStatusIcon key="icon" object={clusterRole} />,
|
||||||
clusterRole.getAge(),
|
clusterRole.getAge(),
|
||||||
|
|||||||
@ -23,7 +23,6 @@ import "./view.scss";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { RouteComponentProps } from "react-router";
|
import type { RouteComponentProps } from "react-router";
|
||||||
import type { RoleBinding } from "../../../api/endpoints";
|
|
||||||
import { KubeObjectListLayout } from "../../kube-object";
|
import { KubeObjectListLayout } from "../../kube-object";
|
||||||
import { KubeObjectStatusIcon } from "../../kube-object-status-icon";
|
import { KubeObjectStatusIcon } from "../../kube-object-status-icon";
|
||||||
import { RoleBindingDialog } from "./dialog";
|
import { RoleBindingDialog } from "./dialog";
|
||||||
@ -55,14 +54,14 @@ export class RoleBindings extends React.Component<Props> {
|
|||||||
store={roleBindingsStore}
|
store={roleBindingsStore}
|
||||||
dependentStores={[rolesStore, clusterRolesStore, serviceAccountsStore]}
|
dependentStores={[rolesStore, clusterRolesStore, serviceAccountsStore]}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (binding: RoleBinding) => binding.getName(),
|
[columnId.name]: binding => binding.getName(),
|
||||||
[columnId.namespace]: (binding: RoleBinding) => binding.getNs(),
|
[columnId.namespace]: binding => binding.getNs(),
|
||||||
[columnId.bindings]: (binding: RoleBinding) => binding.getSubjectNames(),
|
[columnId.bindings]: binding => binding.getSubjectNames(),
|
||||||
[columnId.age]: (binding: RoleBinding) => binding.getTimeDiffFromNow(),
|
[columnId.age]: binding => binding.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(binding: RoleBinding) => binding.getSearchFields(),
|
binding => binding.getSearchFields(),
|
||||||
(binding: RoleBinding) => binding.getSubjectNames(),
|
binding => binding.getSubjectNames(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Role Bindings"
|
renderHeaderTitle="Role Bindings"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -72,7 +71,7 @@ export class RoleBindings extends React.Component<Props> {
|
|||||||
{ title: "Bindings", className: "bindings", sortBy: columnId.bindings, id: columnId.bindings },
|
{ title: "Bindings", className: "bindings", sortBy: columnId.bindings, id: columnId.bindings },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(binding: RoleBinding) => [
|
renderTableContents={binding => [
|
||||||
binding.getName(),
|
binding.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={binding} />,
|
<KubeObjectStatusIcon key="icon" object={binding} />,
|
||||||
binding.getNs(),
|
binding.getNs(),
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import "./view.scss";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { RouteComponentProps } from "react-router";
|
import type { RouteComponentProps } from "react-router";
|
||||||
import type { Role } from "../../../api/endpoints";
|
|
||||||
import { KubeObjectListLayout } from "../../kube-object";
|
import { KubeObjectListLayout } from "../../kube-object";
|
||||||
import { KubeObjectStatusIcon } from "../../kube-object-status-icon";
|
import { KubeObjectStatusIcon } from "../../kube-object-status-icon";
|
||||||
import { AddRoleDialog } from "./add-dialog";
|
import { AddRoleDialog } from "./add-dialog";
|
||||||
@ -51,12 +50,12 @@ export class Roles extends React.Component<Props> {
|
|||||||
className="Roles"
|
className="Roles"
|
||||||
store={rolesStore}
|
store={rolesStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (role: Role) => role.getName(),
|
[columnId.name]: role => role.getName(),
|
||||||
[columnId.namespace]: (role: Role) => role.getNs(),
|
[columnId.namespace]: role => role.getNs(),
|
||||||
[columnId.age]: (role: Role) => role.getTimeDiffFromNow(),
|
[columnId.age]: role => role.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(role: Role) => role.getSearchFields(),
|
role => role.getSearchFields(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Roles"
|
renderHeaderTitle="Roles"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -65,7 +64,7 @@ export class Roles extends React.Component<Props> {
|
|||||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(role: Role) => [
|
renderTableContents={role => [
|
||||||
role.getName(),
|
role.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={role} />,
|
<KubeObjectStatusIcon key="icon" object={role} />,
|
||||||
role.getNs(),
|
role.getNs(),
|
||||||
|
|||||||
@ -54,12 +54,12 @@ export class ServiceAccounts extends React.Component<Props> {
|
|||||||
tableId="access_service_accounts"
|
tableId="access_service_accounts"
|
||||||
className="ServiceAccounts" store={serviceAccountsStore}
|
className="ServiceAccounts" store={serviceAccountsStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (account: ServiceAccount) => account.getName(),
|
[columnId.name]: account => account.getName(),
|
||||||
[columnId.namespace]: (account: ServiceAccount) => account.getNs(),
|
[columnId.namespace]: account => account.getNs(),
|
||||||
[columnId.age]: (account: ServiceAccount) => account.getTimeDiffFromNow(),
|
[columnId.age]: account => account.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(account: ServiceAccount) => account.getSearchFields(),
|
account => account.getSearchFields(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Service Accounts"
|
renderHeaderTitle="Service Accounts"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -68,7 +68,7 @@ export class ServiceAccounts extends React.Component<Props> {
|
|||||||
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
{ title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(account: ServiceAccount) => [
|
renderTableContents={account => [
|
||||||
account.getName(),
|
account.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={account} />,
|
<KubeObjectStatusIcon key="icon" object={account} />,
|
||||||
account.getNs(),
|
account.getNs(),
|
||||||
|
|||||||
@ -31,7 +31,7 @@ import { Notifications } from "../notifications";
|
|||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
import { Input } from "../input";
|
import { Input } from "../input";
|
||||||
import { systemName, maxLength } from "../input/input_validators";
|
import { systemName, maxLength } from "../input/input_validators";
|
||||||
import type { IKubeObjectMetadata } from "../../api/kube-object";
|
import type { KubeObjectMetadata } from "../../api/kube-object";
|
||||||
|
|
||||||
interface Props extends Partial<DialogProps> {
|
interface Props extends Partial<DialogProps> {
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ export class CronJobTriggerDialog extends Component<Props> {
|
|||||||
name: cronjob.metadata.name,
|
name: cronjob.metadata.name,
|
||||||
uid: cronjob.metadata.uid,
|
uid: cronjob.metadata.uid,
|
||||||
}],
|
}],
|
||||||
} as IKubeObjectMetadata
|
} as KubeObjectMetadata
|
||||||
});
|
});
|
||||||
|
|
||||||
close();
|
close();
|
||||||
|
|||||||
@ -62,20 +62,20 @@ export class CronJobs extends React.Component<Props> {
|
|||||||
className="CronJobs" store={cronJobStore}
|
className="CronJobs" store={cronJobStore}
|
||||||
dependentStores={[jobStore, eventStore]}
|
dependentStores={[jobStore, eventStore]}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (cronJob: CronJob) => cronJob.getName(),
|
[columnId.name]: cronJob => cronJob.getName(),
|
||||||
[columnId.namespace]: (cronJob: CronJob) => cronJob.getNs(),
|
[columnId.namespace]: cronJob => cronJob.getNs(),
|
||||||
[columnId.suspend]: (cronJob: CronJob) => cronJob.getSuspendFlag(),
|
[columnId.suspend]: cronJob => cronJob.getSuspendFlag(),
|
||||||
[columnId.active]: (cronJob: CronJob) => cronJobStore.getActiveJobsNum(cronJob),
|
[columnId.active]: cronJob => cronJobStore.getActiveJobsNum(cronJob),
|
||||||
[columnId.lastSchedule]: (cronJob: CronJob) => (
|
[columnId.lastSchedule]: cronJob => (
|
||||||
cronJob.status?.lastScheduleTime
|
cronJob.status?.lastScheduleTime
|
||||||
? moment().diff(cronJob.status.lastScheduleTime)
|
? moment().diff(cronJob.status.lastScheduleTime)
|
||||||
: 0
|
: 0
|
||||||
),
|
),
|
||||||
[columnId.age]: (cronJob: CronJob) => cronJob.getTimeDiffFromNow(),
|
[columnId.age]: cronJob => cronJob.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(cronJob: CronJob) => cronJob.getSearchFields(),
|
cronJob => cronJob.getSearchFields(),
|
||||||
(cronJob: CronJob) => cronJob.getSchedule(),
|
cronJob => cronJob.getSchedule(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Cron Jobs"
|
renderHeaderTitle="Cron Jobs"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -88,7 +88,7 @@ export class CronJobs extends React.Component<Props> {
|
|||||||
{ title: "Last schedule", className: "last-schedule", sortBy: columnId.lastSchedule, id: columnId.lastSchedule },
|
{ title: "Last schedule", className: "last-schedule", sortBy: columnId.lastSchedule, id: columnId.lastSchedule },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(cronJob: CronJob) => [
|
renderTableContents={cronJob => [
|
||||||
cronJob.getName(),
|
cronJob.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={cronJob} />,
|
<KubeObjectStatusIcon key="icon" object={cronJob} />,
|
||||||
cronJob.getNs(),
|
cronJob.getNs(),
|
||||||
|
|||||||
@ -65,14 +65,14 @@ export class DaemonSets extends React.Component<Props> {
|
|||||||
className="DaemonSets" store={daemonSetStore}
|
className="DaemonSets" store={daemonSetStore}
|
||||||
dependentStores={[podsStore, nodesStore, eventStore]}
|
dependentStores={[podsStore, nodesStore, eventStore]}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (daemonSet: DaemonSet) => daemonSet.getName(),
|
[columnId.name]: daemonSet => daemonSet.getName(),
|
||||||
[columnId.namespace]: (daemonSet: DaemonSet) => daemonSet.getNs(),
|
[columnId.namespace]: daemonSet => daemonSet.getNs(),
|
||||||
[columnId.pods]: (daemonSet: DaemonSet) => this.getPodsLength(daemonSet),
|
[columnId.pods]: daemonSet => this.getPodsLength(daemonSet),
|
||||||
[columnId.age]: (daemonSet: DaemonSet) => daemonSet.getTimeDiffFromNow(),
|
[columnId.age]: daemonSet => daemonSet.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(daemonSet: DaemonSet) => daemonSet.getSearchFields(),
|
daemonSet => daemonSet.getSearchFields(),
|
||||||
(daemonSet: DaemonSet) => daemonSet.getLabels(),
|
daemonSet => daemonSet.getLabels(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Daemon Sets"
|
renderHeaderTitle="Daemon Sets"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -83,7 +83,7 @@ export class DaemonSets extends React.Component<Props> {
|
|||||||
{ title: "Node Selector", className: "labels", id: columnId.labels },
|
{ title: "Node Selector", className: "labels", id: columnId.labels },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(daemonSet: DaemonSet) => [
|
renderTableContents={daemonSet => [
|
||||||
daemonSet.getName(),
|
daemonSet.getName(),
|
||||||
daemonSet.getNs(),
|
daemonSet.getNs(),
|
||||||
this.getPodsLength(daemonSet),
|
this.getPodsLength(daemonSet),
|
||||||
|
|||||||
@ -81,15 +81,15 @@ export class Deployments extends React.Component<Props> {
|
|||||||
className="Deployments" store={deploymentStore}
|
className="Deployments" store={deploymentStore}
|
||||||
dependentStores={[replicaSetStore, podsStore, nodesStore, eventStore]}
|
dependentStores={[replicaSetStore, podsStore, nodesStore, eventStore]}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (deployment: Deployment) => deployment.getName(),
|
[columnId.name]: deployment => deployment.getName(),
|
||||||
[columnId.namespace]: (deployment: Deployment) => deployment.getNs(),
|
[columnId.namespace]: deployment => deployment.getNs(),
|
||||||
[columnId.replicas]: (deployment: Deployment) => deployment.getReplicas(),
|
[columnId.replicas]: deployment => deployment.getReplicas(),
|
||||||
[columnId.age]: (deployment: Deployment) => deployment.getTimeDiffFromNow(),
|
[columnId.age]: deployment => deployment.getTimeDiffFromNow(),
|
||||||
[columnId.condition]: (deployment: Deployment) => deployment.getConditionsText(),
|
[columnId.condition]: deployment => deployment.getConditionsText(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(deployment: Deployment) => deployment.getSearchFields(),
|
deployment => deployment.getSearchFields(),
|
||||||
(deployment: Deployment) => deployment.getConditionsText(),
|
deployment => deployment.getConditionsText(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Deployments"
|
renderHeaderTitle="Deployments"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -101,7 +101,7 @@ export class Deployments extends React.Component<Props> {
|
|||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
{ title: "Conditions", className: "conditions", sortBy: columnId.condition, id: columnId.condition },
|
{ title: "Conditions", className: "conditions", sortBy: columnId.condition, id: columnId.condition },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(deployment: Deployment) => [
|
renderTableContents={deployment => [
|
||||||
deployment.getName(),
|
deployment.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={deployment}/>,
|
<KubeObjectStatusIcon key="icon" object={deployment}/>,
|
||||||
deployment.getNs(),
|
deployment.getNs(),
|
||||||
@ -110,9 +110,7 @@ export class Deployments extends React.Component<Props> {
|
|||||||
deployment.getAge(),
|
deployment.getAge(),
|
||||||
this.renderConditions(deployment),
|
this.renderConditions(deployment),
|
||||||
]}
|
]}
|
||||||
renderItemMenu={(item: Deployment) => {
|
renderItemMenu={item => <DeploymentMenu object={item} />}
|
||||||
return <DeploymentMenu object={item}/>;
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,6 @@ import type { RouteComponentProps } from "react-router";
|
|||||||
import { podsStore } from "../+workloads-pods/pods.store";
|
import { podsStore } from "../+workloads-pods/pods.store";
|
||||||
import { jobStore } from "./job.store";
|
import { jobStore } from "./job.store";
|
||||||
import { eventStore } from "../+events/event.store";
|
import { eventStore } from "../+events/event.store";
|
||||||
import type { Job } from "../../api/endpoints/job.api";
|
|
||||||
import { KubeObjectListLayout } from "../kube-object";
|
import { KubeObjectListLayout } from "../kube-object";
|
||||||
import kebabCase from "lodash/kebabCase";
|
import kebabCase from "lodash/kebabCase";
|
||||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||||
@ -54,13 +53,13 @@ export class Jobs extends React.Component<Props> {
|
|||||||
className="Jobs" store={jobStore}
|
className="Jobs" store={jobStore}
|
||||||
dependentStores={[podsStore, eventStore]}
|
dependentStores={[podsStore, eventStore]}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (job: Job) => job.getName(),
|
[columnId.name]: job => job.getName(),
|
||||||
[columnId.namespace]: (job: Job) => job.getNs(),
|
[columnId.namespace]: job => job.getNs(),
|
||||||
[columnId.conditions]: (job: Job) => job.getCondition() != null ? job.getCondition().type : "",
|
[columnId.conditions]: job => job.getCondition() != null ? job.getCondition().type : "",
|
||||||
[columnId.age]: (job: Job) => job.getTimeDiffFromNow(),
|
[columnId.age]: job => job.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(job: Job) => job.getSearchFields(),
|
job => job.getSearchFields(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Jobs"
|
renderHeaderTitle="Jobs"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -71,7 +70,7 @@ export class Jobs extends React.Component<Props> {
|
|||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
{ title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions },
|
{ title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(job: Job) => {
|
renderTableContents={job => {
|
||||||
const condition = job.getCondition();
|
const condition = job.getCondition();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|||||||
@ -47,7 +47,12 @@ const resources: KubeResource[] = [
|
|||||||
export class OverviewStatuses extends React.Component {
|
export class OverviewStatuses extends React.Component {
|
||||||
@boundMethod
|
@boundMethod
|
||||||
renderWorkload(resource: KubeResource): React.ReactElement {
|
renderWorkload(resource: KubeResource): React.ReactElement {
|
||||||
const store = workloadStores[resource];
|
const store = workloadStores.get(resource);
|
||||||
|
|
||||||
|
if (!store) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const items = store.getAllByNs(namespaceStore.contextNamespaces);
|
const items = store.getAllByNs(namespaceStore.contextNamespaces);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -38,13 +38,6 @@ enum sortBy {
|
|||||||
Value = "value",
|
Value = "value",
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortingCallbacks = {
|
|
||||||
[sortBy.Key]: (toleration: IToleration) => toleration.key,
|
|
||||||
[sortBy.Operator]: (toleration: IToleration) => toleration.operator,
|
|
||||||
[sortBy.Effect]: (toleration: IToleration) => toleration.effect,
|
|
||||||
[sortBy.Seconds]: (toleration: IToleration) => toleration.tolerationSeconds,
|
|
||||||
};
|
|
||||||
|
|
||||||
const getTableRow = (toleration: IToleration) => {
|
const getTableRow = (toleration: IToleration) => {
|
||||||
const { key, operator, effect, tolerationSeconds, value } = toleration;
|
const { key, operator, effect, tolerationSeconds, value } = toleration;
|
||||||
|
|
||||||
@ -68,10 +61,17 @@ export function PodTolerations({ tolerations }: Props) {
|
|||||||
<Table
|
<Table
|
||||||
tableId="workloads_pod_tolerations"
|
tableId="workloads_pod_tolerations"
|
||||||
selectable
|
selectable
|
||||||
|
items={tolerations}
|
||||||
scrollable={false}
|
scrollable={false}
|
||||||
sortable={sortingCallbacks}
|
sortable={{
|
||||||
|
[sortBy.Key]: toleration => toleration.key,
|
||||||
|
[sortBy.Operator]: toleration => toleration.operator,
|
||||||
|
[sortBy.Effect]: toleration => toleration.effect,
|
||||||
|
[sortBy.Seconds]: toleration => toleration.tolerationSeconds,
|
||||||
|
}}
|
||||||
sortSyncWithUrl={false}
|
sortSyncWithUrl={false}
|
||||||
className="PodTolerations"
|
className="PodTolerations"
|
||||||
|
renderRow={getTableRow}
|
||||||
>
|
>
|
||||||
<TableHead sticky={false}>
|
<TableHead sticky={false}>
|
||||||
<TableCell className="key" sortBy={sortBy.Key}>Key</TableCell>
|
<TableCell className="key" sortBy={sortBy.Key}>Key</TableCell>
|
||||||
@ -80,9 +80,6 @@ export function PodTolerations({ tolerations }: Props) {
|
|||||||
<TableCell className="effect" sortBy={sortBy.Effect}>Effect</TableCell>
|
<TableCell className="effect" sortBy={sortBy.Effect}>Effect</TableCell>
|
||||||
<TableCell className="seconds" sortBy={sortBy.Seconds}>Seconds</TableCell>
|
<TableCell className="seconds" sortBy={sortBy.Seconds}>Seconds</TableCell>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
{
|
|
||||||
tolerations.map(getTableRow)
|
|
||||||
}
|
|
||||||
</Table>
|
</Table>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -97,21 +97,21 @@ export class Pods extends React.Component<Props> {
|
|||||||
tableId = "workloads_pods"
|
tableId = "workloads_pods"
|
||||||
isConfigurable
|
isConfigurable
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (pod: Pod) => pod.getName(),
|
[columnId.name]: pod => pod.getName(),
|
||||||
[columnId.namespace]: (pod: Pod) => pod.getNs(),
|
[columnId.namespace]: pod => pod.getNs(),
|
||||||
[columnId.containers]: (pod: Pod) => pod.getContainers().length,
|
[columnId.containers]: pod => pod.getContainers().length,
|
||||||
[columnId.restarts]: (pod: Pod) => pod.getRestartsCount(),
|
[columnId.restarts]: pod => pod.getRestartsCount(),
|
||||||
[columnId.owners]: (pod: Pod) => pod.getOwnerRefs().map(ref => ref.kind),
|
[columnId.owners]: pod => pod.getOwnerRefs().map(ref => ref.kind),
|
||||||
[columnId.qos]: (pod: Pod) => pod.getQosClass(),
|
[columnId.qos]: pod => pod.getQosClass(),
|
||||||
[columnId.node]: (pod: Pod) => pod.getNodeName(),
|
[columnId.node]: pod => pod.getNodeName(),
|
||||||
[columnId.age]: (pod: Pod) => pod.getTimeDiffFromNow(),
|
[columnId.age]: pod => pod.getTimeDiffFromNow(),
|
||||||
[columnId.status]: (pod: Pod) => pod.getStatusMessage(),
|
[columnId.status]: pod => pod.getStatusMessage(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(pod: Pod) => pod.getSearchFields(),
|
pod => pod.getSearchFields(),
|
||||||
(pod: Pod) => pod.getStatusMessage(),
|
pod => pod.getStatusMessage(),
|
||||||
(pod: Pod) => pod.status.podIP,
|
pod => pod.status.podIP,
|
||||||
(pod: Pod) => pod.getNodeName(),
|
pod => pod.getNodeName(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Pods"
|
renderHeaderTitle="Pods"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -126,7 +126,7 @@ export class Pods extends React.Component<Props> {
|
|||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(pod: Pod) => [
|
renderTableContents={pod => [
|
||||||
<Badge flat key="name" label={pod.getName()} tooltip={pod.getName()} expandable={false} />,
|
<Badge flat key="name" label={pod.getName()} tooltip={pod.getName()} expandable={false} />,
|
||||||
<KubeObjectStatusIcon key="icon" object={pod} />,
|
<KubeObjectStatusIcon key="icon" object={pod} />,
|
||||||
pod.getNs(),
|
pod.getNs(),
|
||||||
|
|||||||
@ -55,15 +55,15 @@ export class ReplicaSets extends React.Component<Props> {
|
|||||||
tableId="workload_replicasets"
|
tableId="workload_replicasets"
|
||||||
className="ReplicaSets" store={replicaSetStore}
|
className="ReplicaSets" store={replicaSetStore}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (replicaSet: ReplicaSet) => replicaSet.getName(),
|
[columnId.name]: replicaSet => replicaSet.getName(),
|
||||||
[columnId.namespace]: (replicaSet: ReplicaSet) => replicaSet.getNs(),
|
[columnId.namespace]: replicaSet => replicaSet.getNs(),
|
||||||
[columnId.desired]: (replicaSet: ReplicaSet) => replicaSet.getDesired(),
|
[columnId.desired]: replicaSet => replicaSet.getDesired(),
|
||||||
[columnId.current]: (replicaSet: ReplicaSet) => replicaSet.getCurrent(),
|
[columnId.current]: replicaSet => replicaSet.getCurrent(),
|
||||||
[columnId.ready]: (replicaSet: ReplicaSet) => replicaSet.getReady(),
|
[columnId.ready]: replicaSet => replicaSet.getReady(),
|
||||||
[columnId.age]: (replicaSet: ReplicaSet) => replicaSet.getTimeDiffFromNow(),
|
[columnId.age]: replicaSet => replicaSet.getTimeDiffFromNow(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(replicaSet: ReplicaSet) => replicaSet.getSearchFields(),
|
replicaSet => replicaSet.getSearchFields(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Replica Sets"
|
renderHeaderTitle="Replica Sets"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -75,7 +75,7 @@ export class ReplicaSets extends React.Component<Props> {
|
|||||||
{ title: "Ready", className: "ready", sortBy: columnId.ready, id: columnId.ready },
|
{ title: "Ready", className: "ready", sortBy: columnId.ready, id: columnId.ready },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(replicaSet: ReplicaSet) => [
|
renderTableContents={replicaSet => [
|
||||||
replicaSet.getName(),
|
replicaSet.getName(),
|
||||||
<KubeObjectStatusIcon key="icon" object={replicaSet}/>,
|
<KubeObjectStatusIcon key="icon" object={replicaSet}/>,
|
||||||
replicaSet.getNs(),
|
replicaSet.getNs(),
|
||||||
|
|||||||
@ -64,13 +64,13 @@ export class StatefulSets extends React.Component<Props> {
|
|||||||
className="StatefulSets" store={statefulSetStore}
|
className="StatefulSets" store={statefulSetStore}
|
||||||
dependentStores={[podsStore, nodesStore, eventStore]}
|
dependentStores={[podsStore, nodesStore, eventStore]}
|
||||||
sortingCallbacks={{
|
sortingCallbacks={{
|
||||||
[columnId.name]: (statefulSet: StatefulSet) => statefulSet.getName(),
|
[columnId.name]: statefulSet => statefulSet.getName(),
|
||||||
[columnId.namespace]: (statefulSet: StatefulSet) => statefulSet.getNs(),
|
[columnId.namespace]: statefulSet => statefulSet.getNs(),
|
||||||
[columnId.age]: (statefulSet: StatefulSet) => statefulSet.getTimeDiffFromNow(),
|
[columnId.age]: statefulSet => statefulSet.getTimeDiffFromNow(),
|
||||||
[columnId.replicas]: (statefulSet: StatefulSet) => statefulSet.getReplicas(),
|
[columnId.replicas]: statefulSet => statefulSet.getReplicas(),
|
||||||
}}
|
}}
|
||||||
searchFilters={[
|
searchFilters={[
|
||||||
(statefulSet: StatefulSet) => statefulSet.getSearchFields(),
|
statefulSet => statefulSet.getSearchFields(),
|
||||||
]}
|
]}
|
||||||
renderHeaderTitle="Stateful Sets"
|
renderHeaderTitle="Stateful Sets"
|
||||||
renderTableHeader={[
|
renderTableHeader={[
|
||||||
@ -81,7 +81,7 @@ export class StatefulSets extends React.Component<Props> {
|
|||||||
{ className: "warning", showWithColumn: columnId.replicas },
|
{ className: "warning", showWithColumn: columnId.replicas },
|
||||||
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
|
||||||
]}
|
]}
|
||||||
renderTableContents={(statefulSet: StatefulSet) => [
|
renderTableContents={statefulSet => [
|
||||||
statefulSet.getName(),
|
statefulSet.getName(),
|
||||||
statefulSet.getNs(),
|
statefulSet.getNs(),
|
||||||
this.renderPods(statefulSet),
|
this.renderPods(statefulSet),
|
||||||
@ -89,9 +89,7 @@ export class StatefulSets extends React.Component<Props> {
|
|||||||
<KubeObjectStatusIcon key="icon" object={statefulSet}/>,
|
<KubeObjectStatusIcon key="icon" object={statefulSet}/>,
|
||||||
statefulSet.getAge(),
|
statefulSet.getAge(),
|
||||||
]}
|
]}
|
||||||
renderItemMenu={(item: StatefulSet) => {
|
renderItemMenu={item => <StatefulSetMenu object={item} />}
|
||||||
return <StatefulSetMenu object={item}/>;
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,13 +28,14 @@ import { jobStore } from "../+workloads-jobs/job.store";
|
|||||||
import { cronJobStore } from "../+workloads-cronjobs/cronjob.store";
|
import { cronJobStore } from "../+workloads-cronjobs/cronjob.store";
|
||||||
import type { KubeResource } from "../../../common/rbac";
|
import type { KubeResource } from "../../../common/rbac";
|
||||||
import { replicaSetStore } from "../+workloads-replicasets/replicasets.store";
|
import { replicaSetStore } from "../+workloads-replicasets/replicasets.store";
|
||||||
|
import type { KubeObject } from "../../api/kube-object";
|
||||||
|
|
||||||
export const workloadStores: Partial<Record<KubeResource, KubeObjectStore>> = {
|
export const workloadStores = new Map<KubeResource, KubeObjectStore<KubeObject>>([
|
||||||
"pods": podsStore,
|
["pods", podsStore],
|
||||||
"deployments": deploymentStore,
|
["deployments", deploymentStore],
|
||||||
"daemonsets": daemonSetStore,
|
["daemonsets", daemonSetStore],
|
||||||
"statefulsets": statefulSetStore,
|
["statefulsets", statefulSetStore],
|
||||||
"replicasets": replicaSetStore,
|
["replicasets", replicaSetStore],
|
||||||
"jobs": jobStore,
|
["jobs", jobStore],
|
||||||
"cronjobs": cronJobStore,
|
["cronjobs", cronJobStore],
|
||||||
};
|
]);
|
||||||
|
|||||||
@ -84,7 +84,7 @@ export class EditResourceStore extends DockTabStore<EditingResource> {
|
|||||||
return Boolean(tabDataReady && this.getResource(tabId)); // ready to edit resource
|
return Boolean(tabDataReady && this.getResource(tabId)); // ready to edit resource
|
||||||
}
|
}
|
||||||
|
|
||||||
getStore(tabId: TabId): KubeObjectStore | undefined {
|
getStore(tabId: TabId): KubeObjectStore<KubeObject> | undefined {
|
||||||
return apiManager.getStore(this.getResourcePath(tabId));
|
return apiManager.getStore(this.getResourcePath(tabId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@ import React, { ReactNode } from "react";
|
|||||||
import { computed, makeObservable } from "mobx";
|
import { computed, makeObservable } from "mobx";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { ConfirmDialog, ConfirmDialogParams } from "../confirm-dialog";
|
import { ConfirmDialog, ConfirmDialogParams } from "../confirm-dialog";
|
||||||
import { Table, TableCell, TableCellProps, TableHead, TableProps, TableRow, TableRowProps, TableSortCallback } from "../table";
|
import { Table, TableCell, TableCellProps, TableHead, TableProps, TableRow, TableRowProps, TableSortCallbacks } from "../table";
|
||||||
import { boundMethod, createStorage, cssNames, IClassName, isReactNode, noop, ObservableToggleSet, prevDefault, stopPropagation } from "../../utils";
|
import { boundMethod, createStorage, cssNames, IClassName, isReactNode, noop, ObservableToggleSet, prevDefault, stopPropagation } from "../../utils";
|
||||||
import { AddRemoveButtons, AddRemoveButtonsProps } from "../add-remove-buttons";
|
import { AddRemoveButtons, AddRemoveButtonsProps } from "../add-remove-buttons";
|
||||||
import { NoItems } from "../no-items";
|
import { NoItems } from "../no-items";
|
||||||
@ -44,8 +44,10 @@ import { namespaceStore } from "../+namespaces/namespace.store";
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type SearchFilter<T extends ItemObject = any> = (item: T) => string | number | (string | number)[];
|
export type SearchFilter<I extends ItemObject> = (item: I) => string | number | (string | number)[];
|
||||||
export type ItemsFilter<T extends ItemObject = any> = (items: T[]) => T[];
|
export type SearchFilters<I extends ItemObject> = Record<string, SearchFilter<I>>;
|
||||||
|
export type ItemsFilter<I extends ItemObject> = (items: I[]) => I[];
|
||||||
|
export type ItemsFilters<I extends ItemObject> = Record<string, ItemsFilter<I>>;
|
||||||
|
|
||||||
export interface HeaderPlaceholders {
|
export interface HeaderPlaceholders {
|
||||||
title?: ReactNode;
|
title?: ReactNode;
|
||||||
@ -55,23 +57,22 @@ export interface HeaderPlaceholders {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type HeaderCustomizer = (placeholders: HeaderPlaceholders) => HeaderPlaceholders;
|
export type HeaderCustomizer = (placeholders: HeaderPlaceholders) => HeaderPlaceholders;
|
||||||
|
export interface ItemListLayoutProps<I extends ItemObject> {
|
||||||
export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
|
|
||||||
tableId?: string;
|
tableId?: string;
|
||||||
className: IClassName;
|
className: IClassName;
|
||||||
items?: T[];
|
items?: I[];
|
||||||
store: ItemStore<T>;
|
store: ItemStore<I>;
|
||||||
dependentStores?: ItemStore[];
|
dependentStores?: ItemStore<ItemObject>[];
|
||||||
preloadStores?: boolean;
|
preloadStores?: boolean;
|
||||||
hideFilters?: boolean;
|
hideFilters?: boolean;
|
||||||
searchFilters?: SearchFilter<T>[];
|
searchFilters?: SearchFilter<I>[];
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
filterItems?: ItemsFilter<T>[];
|
filterItems?: ItemsFilter<I>[];
|
||||||
|
|
||||||
// header (title, filtering, searching, etc.)
|
// header (title, filtering, searching, etc.)
|
||||||
showHeader?: boolean;
|
showHeader?: boolean;
|
||||||
headerClassName?: IClassName;
|
headerClassName?: IClassName;
|
||||||
renderHeaderTitle?: ReactNode | ((parent: ItemListLayout) => ReactNode);
|
renderHeaderTitle?: ReactNode | ((parent: ItemListLayout<I>) => ReactNode);
|
||||||
customizeHeader?: HeaderCustomizer | HeaderCustomizer[];
|
customizeHeader?: HeaderCustomizer | HeaderCustomizer[];
|
||||||
|
|
||||||
// items list configuration
|
// items list configuration
|
||||||
@ -79,26 +80,28 @@ export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
|
|||||||
isSelectable?: boolean; // show checkbox in rows for selecting items
|
isSelectable?: boolean; // show checkbox in rows for selecting items
|
||||||
isConfigurable?: boolean;
|
isConfigurable?: boolean;
|
||||||
copyClassNameFromHeadCells?: boolean;
|
copyClassNameFromHeadCells?: boolean;
|
||||||
sortingCallbacks?: { [sortBy: string]: TableSortCallback };
|
sortingCallbacks?: TableSortCallbacks<I>;
|
||||||
tableProps?: Partial<TableProps>; // low-level table configuration
|
tableProps?: Partial<TableProps<I>>; // low-level table configuration
|
||||||
renderTableHeader: TableCellProps[] | null;
|
renderTableHeader: TableCellProps[] | null;
|
||||||
renderTableContents: (item: T) => (ReactNode | TableCellProps)[];
|
renderTableContents: (item: I) => (ReactNode | TableCellProps)[];
|
||||||
renderItemMenu?: (item: T, store: ItemStore<T>) => ReactNode;
|
renderItemMenu?: (item: I, store: ItemStore<I>) => ReactNode;
|
||||||
customizeTableRowProps?: (item: T) => Partial<TableRowProps>;
|
customizeTableRowProps?: (item: I) => Partial<TableRowProps>;
|
||||||
addRemoveButtons?: Partial<AddRemoveButtonsProps>;
|
addRemoveButtons?: Partial<AddRemoveButtonsProps>;
|
||||||
virtual?: boolean;
|
virtual?: boolean;
|
||||||
|
|
||||||
// item details view
|
// item details view
|
||||||
hasDetailsView?: boolean;
|
hasDetailsView?: boolean;
|
||||||
detailsItem?: T;
|
detailsItem?: I;
|
||||||
onDetails?: (item: T) => void;
|
onDetails?: (item: I) => void;
|
||||||
|
|
||||||
// other
|
// other
|
||||||
customizeRemoveDialog?: (selectedItems: T[]) => Partial<ConfirmDialogParams>;
|
customizeRemoveDialog?: (selectedItems: I[]) => Partial<ConfirmDialogParams>;
|
||||||
renderFooter?: (parent: ItemListLayout) => React.ReactNode;
|
renderFooter?: (parent: ItemListLayout<I>) => React.ReactNode;
|
||||||
|
|
||||||
|
filterCallbacks?: ItemsFilters<I>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultProps: Partial<ItemListLayoutProps> = {
|
const defaultProps: Partial<ItemListLayoutProps<ItemObject>> = {
|
||||||
showHeader: true,
|
showHeader: true,
|
||||||
isSelectable: true,
|
isSelectable: true,
|
||||||
isConfigurable: false,
|
isConfigurable: false,
|
||||||
@ -115,14 +118,14 @@ const defaultProps: Partial<ItemListLayoutProps> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
export class ItemListLayout<I extends ItemObject> extends React.Component<ItemListLayoutProps<I>> {
|
||||||
static defaultProps = defaultProps as object;
|
static defaultProps = defaultProps as object;
|
||||||
|
|
||||||
private storage = createStorage("item_list_layout", {
|
private storage = createStorage("item_list_layout", {
|
||||||
showFilters: false, // setup defaults
|
showFilters: false, // setup defaults
|
||||||
});
|
});
|
||||||
|
|
||||||
constructor(props: ItemListLayoutProps) {
|
constructor(props: ItemListLayoutProps<I>) {
|
||||||
super(props);
|
super(props);
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
}
|
}
|
||||||
@ -158,7 +161,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
|||||||
stores.forEach(store => store.loadAll(namespaceStore.contextNamespaces));
|
stores.forEach(store => store.loadAll(namespaceStore.contextNamespaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
private filterCallbacks: { [type: string]: ItemsFilter } = {
|
private filterCallbacks: ItemsFilters<I> = {
|
||||||
[FilterType.SEARCH]: items => {
|
[FilterType.SEARCH]: items => {
|
||||||
const { searchFilters } = this.props;
|
const { searchFilters } = this.props;
|
||||||
const search = pageFilters.getValues(FilterType.SEARCH)[0] || "";
|
const search = pageFilters.getValues(FilterType.SEARCH)[0] || "";
|
||||||
@ -199,20 +202,20 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
|||||||
return activeFilters;
|
return activeFilters;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyFilters<T>(filters: ItemsFilter[], items: T[]): T[] {
|
applyFilters(filters: ItemsFilter<I>[], items: I[]): I[] {
|
||||||
if (!filters || !filters.length) return items;
|
if (!filters || !filters.length) return items;
|
||||||
|
|
||||||
return filters.reduce((items, filter) => filter(items), items);
|
return filters.reduce((items, filter) => filter(items), items);
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get items() {
|
@computed get items() {
|
||||||
const { filters, filterCallbacks } = this;
|
const { filters, filterCallbacks, props } = this;
|
||||||
const filterGroups = groupBy<Filter>(filters, ({ type }) => type);
|
const filterGroups = groupBy<Filter>(filters, ({ type }) => type);
|
||||||
|
|
||||||
const filterItems: ItemsFilter[] = [];
|
const filterItems: ItemsFilter<I>[] = [];
|
||||||
|
|
||||||
Object.entries(filterGroups).forEach(([type, filtersGroup]) => {
|
Object.entries(filterGroups).forEach(([type, filtersGroup]) => {
|
||||||
const filterCallback = filterCallbacks[type];
|
const filterCallback = filterCallbacks[type] ?? props.filterCallbacks?.[type];
|
||||||
|
|
||||||
if (filterCallback && filtersGroup.length > 0) {
|
if (filterCallback && filtersGroup.length > 0) {
|
||||||
filterItems.push(filterCallback);
|
filterItems.push(filterCallback);
|
||||||
|
|||||||
@ -30,9 +30,7 @@ import type { KubeObject } from "../../api/kube-object";
|
|||||||
import { Spinner } from "../spinner";
|
import { Spinner } from "../spinner";
|
||||||
import { apiManager } from "../../api/api-manager";
|
import { apiManager } from "../../api/api-manager";
|
||||||
import { crdStore } from "../+custom-resources/crd.store";
|
import { crdStore } from "../+custom-resources/crd.store";
|
||||||
import { CrdResourceDetails } from "../+custom-resources";
|
|
||||||
import { KubeObjectMenu } from "./kube-object-menu";
|
import { KubeObjectMenu } from "./kube-object-menu";
|
||||||
import type { CustomResourceDefinition } from "../../api/endpoints";
|
|
||||||
import { KubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
|
import { KubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
|
||||||
import logger from "../../../main/logger";
|
import logger from "../../../main/logger";
|
||||||
|
|
||||||
@ -108,6 +106,8 @@ export class KubeObjectDetails extends React.Component {
|
|||||||
?.getByPath(this.path);
|
?.getByPath(this.path);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[KUBE-OBJECT-DETAILS]: failed to get store or object: ${error}`, { path: this.path });
|
logger.error(`[KUBE-OBJECT-DETAILS]: failed to get store or object: ${error}`, { path: this.path });
|
||||||
|
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ export class KubeObjectDetails extends React.Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { object, isLoading, loadingError, isCrdInstance } = this;
|
const { object, isLoading, loadingError } = this;
|
||||||
const isOpen = !!(object || isLoading || loadingError);
|
const isOpen = !!(object || isLoading || loadingError);
|
||||||
|
|
||||||
if (!object) {
|
if (!object) {
|
||||||
@ -169,10 +169,6 @@ export class KubeObjectDetails extends React.Component {
|
|||||||
<item.components.Details object={object} key={`object-details-${index}`} />
|
<item.components.Details object={object} key={`object-details-${index}`} />
|
||||||
));
|
));
|
||||||
|
|
||||||
if (isCrdInstance && details.length === 0) {
|
|
||||||
details.push(<CrdResourceDetails object={object as CustomResourceDefinition}/>);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
className="KubeObjectDetails flex column"
|
className="KubeObjectDetails flex column"
|
||||||
|
|||||||
@ -33,20 +33,20 @@ import { clusterContext } from "../context";
|
|||||||
import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter";
|
import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter";
|
||||||
import { ResourceKindMap, ResourceNames } from "../../utils/rbac";
|
import { ResourceKindMap, ResourceNames } from "../../utils/rbac";
|
||||||
|
|
||||||
export interface KubeObjectListLayoutProps extends ItemListLayoutProps {
|
export interface KubeObjectListLayoutProps<K extends KubeObject> extends ItemListLayoutProps<K> {
|
||||||
store: KubeObjectStore;
|
store: KubeObjectStore<K>;
|
||||||
dependentStores?: KubeObjectStore[];
|
dependentStores?: KubeObjectStore<KubeObject>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultProps: Partial<KubeObjectListLayoutProps> = {
|
const defaultProps: Partial<KubeObjectListLayoutProps<KubeObject>> = {
|
||||||
onDetails: (item: KubeObject) => showDetails(item.selfLink),
|
onDetails: (item: KubeObject) => showDetails(item.selfLink),
|
||||||
};
|
};
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class KubeObjectListLayout extends React.Component<KubeObjectListLayoutProps> {
|
export class KubeObjectListLayout<K extends KubeObject> extends React.Component<KubeObjectListLayoutProps<K>> {
|
||||||
static defaultProps = defaultProps as object;
|
static defaultProps = defaultProps as object;
|
||||||
|
|
||||||
constructor(props: KubeObjectListLayoutProps) {
|
constructor(props: KubeObjectListLayoutProps<K>) {
|
||||||
super(props);
|
super(props);
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
}
|
}
|
||||||
@ -95,7 +95,7 @@ export class KubeObjectListLayout extends React.Component<KubeObjectListLayoutPr
|
|||||||
}),
|
}),
|
||||||
...[customizeHeader].flat(),
|
...[customizeHeader].flat(),
|
||||||
]}
|
]}
|
||||||
renderItemMenu={(item: KubeObject) => <KubeObjectMenu object={item} />} // safe because we are dealing with KubeObjects here
|
renderItemMenu={item => <KubeObjectMenu object={item} />}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { IKubeMetaField, KubeObject } from "../../api/kube-object";
|
import type { KubeMetaField, KubeObject } from "../../api/kube-object";
|
||||||
import { DrawerItem, DrawerItemLabels } from "../drawer";
|
import { DrawerItem, DrawerItemLabels } from "../drawer";
|
||||||
import { lookupApiLink } from "../../api/kube-api";
|
import { lookupApiLink } from "../../api/kube-api";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
@ -30,15 +30,15 @@ import { getDetailsUrl } from "./kube-object-details";
|
|||||||
|
|
||||||
export interface KubeObjectMetaProps {
|
export interface KubeObjectMetaProps {
|
||||||
object: KubeObject;
|
object: KubeObject;
|
||||||
hideFields?: IKubeMetaField[];
|
hideFields?: KubeMetaField[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KubeObjectMeta extends React.Component<KubeObjectMetaProps> {
|
export class KubeObjectMeta extends React.Component<KubeObjectMetaProps> {
|
||||||
static defaultHiddenFields: IKubeMetaField[] = [
|
static defaultHiddenFields: KubeMetaField[] = [
|
||||||
"uid", "resourceVersion", "selfLink"
|
"uid", "resourceVersion", "selfLink"
|
||||||
];
|
];
|
||||||
|
|
||||||
isHidden(field: IKubeMetaField): boolean {
|
isHidden(field: KubeMetaField): boolean {
|
||||||
const { hideFields = KubeObjectMeta.defaultHiddenFields } = this.props;
|
const { hideFields = KubeObjectMeta.defaultHiddenFields } = this.props;
|
||||||
|
|
||||||
return hideFields.includes(field);
|
return hideFields.includes(field);
|
||||||
|
|||||||
@ -30,19 +30,18 @@ import { TableHead, TableHeadElem, TableHeadProps } from "./table-head";
|
|||||||
import type { TableCellElem } from "./table-cell";
|
import type { TableCellElem } from "./table-cell";
|
||||||
import { VirtualList } from "../virtual-list";
|
import { VirtualList } from "../virtual-list";
|
||||||
import { createPageParam } from "../../navigation";
|
import { createPageParam } from "../../navigation";
|
||||||
import type { ItemObject } from "../../item.store";
|
|
||||||
import { getSortParams, setSortParams } from "./table.storage";
|
import { getSortParams, setSortParams } from "./table.storage";
|
||||||
import { computed, makeObservable } from "mobx";
|
import { computed, makeObservable } from "mobx";
|
||||||
|
|
||||||
export type TableSortBy = string;
|
export type TableSortBy = string;
|
||||||
export type TableOrderBy = "asc" | "desc" | string;
|
export type TableOrderBy = "asc" | "desc" | string;
|
||||||
export type TableSortParams = { sortBy: TableSortBy; orderBy: TableOrderBy };
|
export type TableSortParams = { sortBy: TableSortBy; orderBy: TableOrderBy };
|
||||||
export type TableSortCallback<D = any> = (data: D) => string | number | (string | number)[];
|
export type TableSortCallback<Item> = (data: Item) => string | number | (string | number)[];
|
||||||
export type TableSortCallbacks = { [columnId: string]: TableSortCallback };
|
export type TableSortCallbacks<Item> = Record<string, TableSortCallback<Item>>;
|
||||||
|
|
||||||
export interface TableProps extends React.DOMAttributes<HTMLDivElement> {
|
export interface TableProps<Item> extends React.DOMAttributes<HTMLDivElement> {
|
||||||
tableId?: string;
|
tableId?: string;
|
||||||
items?: ItemObject[]; // Raw items data
|
items?: Item[]; // Raw items data
|
||||||
className?: string;
|
className?: string;
|
||||||
autoSize?: boolean; // Setup auto-sizing for all columns (flex: 1 0)
|
autoSize?: boolean; // Setup auto-sizing for all columns (flex: 1 0)
|
||||||
selectable?: boolean; // Highlight rows on hover
|
selectable?: boolean; // Highlight rows on hover
|
||||||
@ -52,7 +51,7 @@ export interface TableProps extends React.DOMAttributes<HTMLDivElement> {
|
|||||||
* Define sortable callbacks for every column in <TableHead><TableCell sortBy="someCol"><TableHead>
|
* Define sortable callbacks for every column in <TableHead><TableCell sortBy="someCol"><TableHead>
|
||||||
* @sortItem argument in the callback is an object, provided in <TableRow sortItem={someColDataItem}/>
|
* @sortItem argument in the callback is an object, provided in <TableRow sortItem={someColDataItem}/>
|
||||||
*/
|
*/
|
||||||
sortable?: TableSortCallbacks;
|
sortable?: TableSortCallbacks<Item>;
|
||||||
sortSyncWithUrl?: boolean; // sorting state is managed globally from url params
|
sortSyncWithUrl?: boolean; // sorting state is managed globally from url params
|
||||||
sortByDefault?: Partial<TableSortParams>; // default sorting params
|
sortByDefault?: Partial<TableSortParams>; // default sorting params
|
||||||
onSort?: (params: TableSortParams) => void; // callback on sort change, default: global sync with url
|
onSort?: (params: TableSortParams) => void; // callback on sort change, default: global sync with url
|
||||||
@ -61,8 +60,9 @@ export interface TableProps extends React.DOMAttributes<HTMLDivElement> {
|
|||||||
virtual?: boolean; // Use virtual list component to render only visible rows
|
virtual?: boolean; // Use virtual list component to render only visible rows
|
||||||
rowPadding?: string;
|
rowPadding?: string;
|
||||||
rowLineHeight?: string;
|
rowLineHeight?: string;
|
||||||
customRowHeights?: (item: object, lineHeight: number, paddings: number) => number;
|
customRowHeights?: (item: Item, lineHeight: number, paddings: number) => number;
|
||||||
getTableRow?: (uid: string) => React.ReactElement<TableRowProps>;
|
getTableRow?: (uid: string) => React.ReactElement<TableRowProps>;
|
||||||
|
renderRow?: (item: Item) => React.ReactElement<TableRowProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sortByUrlParam = createPageParam({
|
export const sortByUrlParam = createPageParam({
|
||||||
@ -74,8 +74,8 @@ export const orderByUrlParam = createPageParam({
|
|||||||
});
|
});
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class Table extends React.Component<TableProps> {
|
export class Table<Item> extends React.Component<TableProps<Item>> {
|
||||||
static defaultProps: TableProps = {
|
static defaultProps: TableProps<any> = {
|
||||||
scrollable: true,
|
scrollable: true,
|
||||||
autoSize: true,
|
autoSize: true,
|
||||||
rowPadding: "8px",
|
rowPadding: "8px",
|
||||||
@ -83,7 +83,7 @@ export class Table extends React.Component<TableProps> {
|
|||||||
sortSyncWithUrl: true,
|
sortSyncWithUrl: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: TableProps) {
|
constructor(props: TableProps<Item>) {
|
||||||
super(props);
|
super(props);
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
}
|
}
|
||||||
@ -171,9 +171,20 @@ export class Table extends React.Component<TableProps> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderRows() {
|
private getContent() {
|
||||||
const { sortable, noItems, children, virtual, customRowHeights, rowLineHeight, rowPadding, items, getTableRow, selectedItemId, className } = this.props;
|
const { items, renderRow, children } = this.props;
|
||||||
const content = React.Children.toArray(children) as (TableRowElem | TableHeadElem)[];
|
const content = React.Children.toArray(children) as (TableRowElem | TableHeadElem)[];
|
||||||
|
|
||||||
|
if (renderRow) {
|
||||||
|
content.push(...items.map(renderRow));
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRows() {
|
||||||
|
const { sortable, noItems, virtual, customRowHeights, rowLineHeight, rowPadding, items, getTableRow, selectedItemId, className } = this.props;
|
||||||
|
const content = this.getContent();
|
||||||
let rows: React.ReactElement<TableRowProps>[] = content.filter(elem => elem.type === TableRow);
|
let rows: React.ReactElement<TableRowProps>[] = content.filter(elem => elem.type === TableRow);
|
||||||
let sortedItems = rows.length ? rows.map(row => row.props.sortItem) : [...items];
|
let sortedItems = rows.length ? rows.map(row => row.props.sortItem) : [...items];
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,7 @@ import { ConfigMapDetails } from "../components/+config-maps";
|
|||||||
import { PodDisruptionBudgetDetails } from "../components/+config-pod-disruption-budgets";
|
import { PodDisruptionBudgetDetails } from "../components/+config-pod-disruption-budgets";
|
||||||
import { ResourceQuotaDetails } from "../components/+config-resource-quotas";
|
import { ResourceQuotaDetails } from "../components/+config-resource-quotas";
|
||||||
import { SecretDetails } from "../components/+config-secrets";
|
import { SecretDetails } from "../components/+config-secrets";
|
||||||
import { CRDDetails } from "../components/+custom-resources";
|
import { CRDDetails, CrdResourceDetails } from "../components/+custom-resources";
|
||||||
import { EventDetails } from "../components/+events";
|
import { EventDetails } from "../components/+events";
|
||||||
import { KubeEventDetails } from "../components/+events/kube-event-details";
|
import { KubeEventDetails } from "../components/+events/kube-event-details";
|
||||||
import { NamespaceDetails } from "../components/+namespaces";
|
import { NamespaceDetails } from "../components/+namespaces";
|
||||||
@ -55,6 +55,7 @@ import { PodDetails } from "../components/+workloads-pods";
|
|||||||
import { ReplicaSetDetails } from "../components/+workloads-replicasets";
|
import { ReplicaSetDetails } from "../components/+workloads-replicasets";
|
||||||
import { StatefulSetDetails } from "../components/+workloads-statefulsets";
|
import { StatefulSetDetails } from "../components/+workloads-statefulsets";
|
||||||
import type { KubeObjectDetailsProps } from "../components/kube-object";
|
import type { KubeObjectDetailsProps } from "../components/kube-object";
|
||||||
|
import type { CustomResourceDefinition } from "../api/endpoints";
|
||||||
|
|
||||||
export function intiKubeObjectDetailRegistry() {
|
export function intiKubeObjectDetailRegistry() {
|
||||||
KubeObjectDetailRegistry.getInstance()
|
KubeObjectDetailRegistry.getInstance()
|
||||||
@ -444,6 +445,13 @@ export function intiKubeObjectDetailRegistry() {
|
|||||||
components: {
|
components: {
|
||||||
Details: (props: KubeObjectDetailsProps<KubeObject>) => <KubeEventDetails {...props}/>,
|
Details: (props: KubeObjectDetailsProps<KubeObject>) => <KubeEventDetails {...props}/>,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "CustomResourceDefinition",
|
||||||
|
apiVersions: ["apiextensions.k8s.io/v1"],
|
||||||
|
components: {
|
||||||
|
Details: (props: KubeObjectDetailsProps<CustomResourceDefinition>) => <CrdResourceDetails {...props}/>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,15 +28,15 @@ export interface ItemObject {
|
|||||||
getName(): string;
|
getName(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class ItemStore<T extends ItemObject = ItemObject> {
|
export abstract class ItemStore<Item extends ItemObject> {
|
||||||
abstract loadAll(...args: any[]): Promise<void | T[]>;
|
abstract loadAll(...args: any[]): Promise<void | Item[]>;
|
||||||
|
|
||||||
protected defaultSorting = (item: T) => item.getName();
|
protected defaultSorting = (item: Item) => item.getName();
|
||||||
|
|
||||||
@observable failedLoading = false;
|
@observable failedLoading = false;
|
||||||
@observable isLoading = false;
|
@observable isLoading = false;
|
||||||
@observable isLoaded = false;
|
@observable isLoaded = false;
|
||||||
@observable items = observable.array<T>([], { deep: false });
|
@observable items = observable.array<Item>([], { deep: false });
|
||||||
@observable selectedItemsIds = observable.map<string, boolean>();
|
@observable selectedItemsIds = observable.map<string, boolean>();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -44,11 +44,11 @@ export abstract class ItemStore<T extends ItemObject = ItemObject> {
|
|||||||
autoBind(this);
|
autoBind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get selectedItems(): T[] {
|
@computed get selectedItems(): Item[] {
|
||||||
return this.items.filter(item => this.selectedItemsIds.get(item.getId()));
|
return this.items.filter(item => this.selectedItemsIds.get(item.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public getItems(): T[] {
|
public getItems(): Item[] {
|
||||||
return Array.from(this.items);
|
return Array.from(this.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,8 +56,8 @@ export abstract class ItemStore<T extends ItemObject = ItemObject> {
|
|||||||
return this.items.length;
|
return this.items.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
getByName(name: string, ...args: any[]): T;
|
getByName(name: string, ...args: any[]): Item;
|
||||||
getByName(name: string): T {
|
getByName(name: string): Item {
|
||||||
return this.items.find(item => item.getName() === name);
|
return this.items.find(item => item.getName() === name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,13 +75,13 @@ export abstract class ItemStore<T extends ItemObject = ItemObject> {
|
|||||||
* @param order whether to sort from least to greatest (`"asc"` (default)) or vice-versa (`"desc"`)
|
* @param order whether to sort from least to greatest (`"asc"` (default)) or vice-versa (`"desc"`)
|
||||||
*/
|
*/
|
||||||
@action
|
@action
|
||||||
protected sortItems(items: T[] = this.items, sorting: ((item: T) => any)[] = [this.defaultSorting], order?: "asc" | "desc"): T[] {
|
protected sortItems(items: Item[] = this.items, sorting: ((item: Item) => any)[] = [this.defaultSorting], order?: "asc" | "desc"): Item[] {
|
||||||
return orderBy(items, sorting, order);
|
return orderBy(items, sorting, order);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async createItem(...args: any[]): Promise<any>;
|
protected async createItem(...args: any[]): Promise<any>;
|
||||||
@action
|
@action
|
||||||
protected async createItem(request: () => Promise<T>) {
|
protected async createItem(request: () => Promise<Item>) {
|
||||||
const newItem = await request();
|
const newItem = await request();
|
||||||
const item = this.items.find(item => item.getId() === newItem.getId());
|
const item = this.items.find(item => item.getId() === newItem.getId());
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ export abstract class ItemStore<T extends ItemObject = ItemObject> {
|
|||||||
|
|
||||||
protected async loadItems(...args: any[]): Promise<any>;
|
protected async loadItems(...args: any[]): Promise<any>;
|
||||||
@action
|
@action
|
||||||
protected async loadItems(request: () => Promise<T[] | any>, sortItems = true) {
|
protected async loadItems(request: () => Promise<Item[] | any>, sortItems = true) {
|
||||||
if (this.isLoading) {
|
if (this.isLoading) {
|
||||||
await when(() => !this.isLoading);
|
await when(() => !this.isLoading);
|
||||||
|
|
||||||
@ -117,9 +117,9 @@ export abstract class ItemStore<T extends ItemObject = ItemObject> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async loadItem(...args: any[]): Promise<T>
|
protected async loadItem(...args: any[]): Promise<Item>
|
||||||
@action
|
@action
|
||||||
protected async loadItem(request: () => Promise<T>, sortItems = true) {
|
protected async loadItem(request: () => Promise<Item>, sortItems = true) {
|
||||||
const item = await Promise.resolve(request()).catch(() => null);
|
const item = await Promise.resolve(request()).catch(() => null);
|
||||||
|
|
||||||
if (item) {
|
if (item) {
|
||||||
@ -141,7 +141,7 @@ export abstract class ItemStore<T extends ItemObject = ItemObject> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
protected async updateItem(item: T, request: () => Promise<T>) {
|
protected async updateItem(item: Item, request: () => Promise<Item>) {
|
||||||
const updatedItem = await request();
|
const updatedItem = await request();
|
||||||
const index = this.items.findIndex(i => i.getId() === item.getId());
|
const index = this.items.findIndex(i => i.getId() === item.getId());
|
||||||
|
|
||||||
@ -151,28 +151,28 @@ export abstract class ItemStore<T extends ItemObject = ItemObject> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
protected async removeItem(item: T, request: () => Promise<any>) {
|
protected async removeItem(item: Item, request: () => Promise<any>) {
|
||||||
await request();
|
await request();
|
||||||
this.items.remove(item);
|
this.items.remove(item);
|
||||||
this.selectedItemsIds.delete(item.getId());
|
this.selectedItemsIds.delete(item.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
isSelected(item: T) {
|
isSelected(item: Item) {
|
||||||
return !!this.selectedItemsIds.get(item.getId());
|
return !!this.selectedItemsIds.get(item.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
select(item: T) {
|
select(item: Item) {
|
||||||
this.selectedItemsIds.set(item.getId(), true);
|
this.selectedItemsIds.set(item.getId(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
unselect(item: T) {
|
unselect(item: Item) {
|
||||||
this.selectedItemsIds.delete(item.getId());
|
this.selectedItemsIds.delete(item.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
toggleSelection(item: T) {
|
toggleSelection(item: Item) {
|
||||||
if (this.isSelected(item)) {
|
if (this.isSelected(item)) {
|
||||||
this.unselect(item);
|
this.unselect(item);
|
||||||
} else {
|
} else {
|
||||||
@ -181,7 +181,7 @@ export abstract class ItemStore<T extends ItemObject = ItemObject> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
toggleSelectionAll(visibleItems: T[] = this.items) {
|
toggleSelectionAll(visibleItems: Item[] = this.items) {
|
||||||
const allSelected = visibleItems.every(this.isSelected);
|
const allSelected = visibleItems.every(this.isSelected);
|
||||||
|
|
||||||
if (allSelected) {
|
if (allSelected) {
|
||||||
@ -191,7 +191,7 @@ export abstract class ItemStore<T extends ItemObject = ItemObject> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isSelectedAll(visibleItems: T[] = this.items) {
|
isSelectedAll(visibleItems: Item[] = this.items) {
|
||||||
if (!visibleItems.length) return false;
|
if (!visibleItems.length) return false;
|
||||||
|
|
||||||
return visibleItems.every(this.isSelected);
|
return visibleItems.every(this.isSelected);
|
||||||
|
|||||||
@ -31,13 +31,13 @@ import { ensureObjectSelfLink, IKubeApiQueryParams, KubeApi, parseKubeApi } from
|
|||||||
import type { KubeJsonApiData } from "./api/kube-json-api";
|
import type { KubeJsonApiData } from "./api/kube-json-api";
|
||||||
import { Notifications } from "./components/notifications";
|
import { Notifications } from "./components/notifications";
|
||||||
|
|
||||||
export interface KubeObjectStoreLoadingParams {
|
export interface KubeObjectStoreLoadingParams<K extends KubeObject> {
|
||||||
namespaces: string[];
|
namespaces: string[];
|
||||||
api?: KubeApi;
|
api?: KubeApi<K>;
|
||||||
reqInit?: RequestInit;
|
reqInit?: RequestInit;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemStore<T> {
|
export abstract class KubeObjectStore<T extends KubeObject> extends ItemStore<T> {
|
||||||
static defaultContext = observable.box<ClusterContext>(); // TODO: support multiple cluster contexts
|
static defaultContext = observable.box<ClusterContext>(); // TODO: support multiple cluster contexts
|
||||||
|
|
||||||
abstract api: KubeApi<T>;
|
abstract api: KubeApi<T>;
|
||||||
@ -137,7 +137,7 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async loadItems({ namespaces, api, reqInit }: KubeObjectStoreLoadingParams): Promise<T[]> {
|
protected async loadItems({ namespaces, api, reqInit }: KubeObjectStoreLoadingParams<T>): Promise<T[]> {
|
||||||
if (this.context?.cluster.isAllowedResource(api.kind)) {
|
if (this.context?.cluster.isAllowedResource(api.kind)) {
|
||||||
if (!api.isNamespaced) {
|
if (!api.isNamespaced) {
|
||||||
return api.list({ reqInit }, this.query);
|
return api.list({ reqInit }, this.query);
|
||||||
@ -279,7 +279,7 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update(item: T, data: Partial<T>): Promise<T> {
|
async update(item: T, data: Partial<T>): Promise<T> {
|
||||||
const newItem = await item.update<T>(data);
|
const newItem = await item.update(data);
|
||||||
|
|
||||||
ensureObjectSelfLink(this.api, newItem);
|
ensureObjectSelfLink(this.api, newItem);
|
||||||
|
|
||||||
@ -345,7 +345,7 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
|
|||||||
|
|
||||||
const { signal } = abortController;
|
const { signal } = abortController;
|
||||||
|
|
||||||
const callback = (data: IKubeWatchEvent, error: any) => {
|
const callback = (data: IKubeWatchEvent<T>, error: any) => {
|
||||||
if (!this.isLoaded || error instanceof DOMException) return;
|
if (!this.isLoaded || error instanceof DOMException) return;
|
||||||
|
|
||||||
if (error instanceof Response) {
|
if (error instanceof Response) {
|
||||||
@ -393,7 +393,7 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case "ADDED":
|
case "ADDED":
|
||||||
case "MODIFIED":
|
case "MODIFIED":
|
||||||
const newItem = new api.objectConstructor(object);
|
const newItem = new api.objectConstructor(object) as T;
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
items.push(newItem);
|
items.push(newItem);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user