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

Merge branch 'master' into preferences-redesign

This commit is contained in:
Alex Andreev 2021-04-05 16:22:15 +03:00
commit d0078519cb
16 changed files with 64 additions and 39 deletions

View File

@ -518,7 +518,8 @@ export class Cluster implements ClusterModel, ClusterState {
timeout: 0, timeout: 0,
resolveWithFullResponse: false, resolveWithFullResponse: false,
json: true, json: true,
qs: queryParams, method: "POST",
form: queryParams,
}); });
} }

View File

@ -26,7 +26,7 @@ export class NodeShellSession extends ShellSession {
if (this.createNodeShellPod(this.podId, this.nodeName)) { if (this.createNodeShellPod(this.podId, this.nodeName)) {
await this.waitForRunningPod(this.podId).catch(() => { await this.waitForRunningPod(this.podId).catch(() => {
this.exit(1001); this.exitAndClean(1001);
}); });
} }
args = ["exec", "-i", "-t", "-n", "kube-system", this.podId, "--", "sh", "-c", "((clear && bash) || (clear && ash) || (clear && sh))"]; args = ["exec", "-i", "-t", "-n", "kube-system", this.podId, "--", "sh", "-c", "((clear && bash) || (clear && ash) || (clear && sh))"];
@ -49,11 +49,14 @@ export class NodeShellSession extends ShellSession {
appEventBus.emit({name: "node-shell", action: "open"}); appEventBus.emit({name: "node-shell", action: "open"});
} }
protected exit(code = 1000) { protected exitAndClean(code = 1000) {
if (this.podId) { if (this.podId) {
this.deleteNodeShellPod(); this.deleteNodeShellPod();
} }
super.exit(code);
if (code != 1000) {
this.sendResponse("Error occurred. ");
}
} }
protected async createNodeShellPod(podId: string, nodeName: string) { protected async createNodeShellPod(podId: string, nodeName: string) {
@ -65,6 +68,7 @@ export class NodeShellSession extends ShellSession {
namespace: "kube-system" namespace: "kube-system"
}, },
spec: { spec: {
nodeName,
restartPolicy: "Never", restartPolicy: "Never",
terminationGracePeriodSeconds: 0, terminationGracePeriodSeconds: 0,
hostPID: true, hostPID: true,
@ -82,9 +86,6 @@ export class NodeShellSession extends ShellSession {
command: ["nsenter"], command: ["nsenter"],
args: ["-t", "1", "-m", "-u", "-i", "-n", "sleep", "14000"] args: ["-t", "1", "-m", "-u", "-i", "-n", "sleep", "14000"]
}], }],
nodeSelector: {
"kubernetes.io/hostname": nodeName
}
} }
} as k8s.V1Pod; } as k8s.V1Pod;

View File

@ -134,6 +134,7 @@ export class ClusterIssues extends React.Component<Props> {
<>Warnings: {warnings.length}</> <>Warnings: {warnings.length}</>
</SubHeader> </SubHeader>
<Table <Table
tableId="cluster_issues"
items={warnings} items={warnings}
virtual virtual
selectable selectable

View File

@ -5,7 +5,7 @@ import { Namespace, namespacesApi } from "../../api/endpoints/namespaces.api";
import { createPageParam } from "../../navigation"; import { createPageParam } from "../../navigation";
import { apiManager } from "../../api/api-manager"; import { apiManager } from "../../api/api-manager";
const selectedNamespaces = createStorage<string[]>("selected_namespaces"); const selectedNamespaces = createStorage<string[] | undefined>("selected_namespaces", undefined);
export const namespaceUrlParam = createPageParam<string[]>({ export const namespaceUrlParam = createPageParam<string[]>({
name: "namespaces", name: "namespaces",

View File

@ -20,7 +20,7 @@ export class PodSecurityPolicies extends React.Component {
return ( return (
<KubeObjectListLayout <KubeObjectListLayout
isConfigurable isConfigurable
tableId="access_roles" tableId="access_pod_security_policies"
className="PodSecurityPolicies" className="PodSecurityPolicies"
isClusterScoped={true} isClusterScoped={true}
store={podSecurityPoliciesStore} store={podSecurityPoliciesStore}

View File

@ -64,6 +64,7 @@ export class VolumeDetailsList extends React.Component<Props> {
<div className="VolumeDetailsList flex column"> <div className="VolumeDetailsList flex column">
<DrawerTitle title="Persistent Volumes"/> <DrawerTitle title="Persistent Volumes"/>
<Table <Table
tableId="storage_volume_details_list"
items={persistentVolumes} items={persistentVolumes}
selectable selectable
virtual={virtual} virtual={virtual}

View File

@ -135,6 +135,7 @@ export class PodDetailsList extends React.Component<Props> {
<div className="PodDetailsList flex column"> <div className="PodDetailsList flex column">
{showTitle && <DrawerTitle title="Pods"/>} {showTitle && <DrawerTitle title="Pods"/>}
<Table <Table
tableId="workloads_pod_details_list"
items={pods} items={pods}
selectable selectable
virtual={virtual} virtual={virtual}

View File

@ -43,6 +43,7 @@ const getTableRow = (toleration: IToleration) => {
export function PodTolerations({ tolerations }: Props) { export function PodTolerations({ tolerations }: Props) {
return ( return (
<Table <Table
tableId="workloads_pod_tolerations"
selectable selectable
scrollable={false} scrollable={false}
sortable={sortingCallbacks} sortable={sortingCallbacks}

View File

@ -81,6 +81,6 @@ export class DockTabStore<T> {
reset() { reset() {
this.data.clear(); this.data.clear();
this.storage?.clear(); this.storage?.reset();
} }
} }

View File

@ -413,7 +413,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
renderList() { renderList() {
const { const {
store, hasDetailsView, addRemoveButtons = {}, virtual, sortingCallbacks, detailsItem, store, hasDetailsView, addRemoveButtons = {}, virtual, sortingCallbacks, detailsItem,
tableProps = {}, tableProps = {}, tableId
} = this.props; } = this.props;
const { isReady, removeItemsDialog, items } = this; const { isReady, removeItemsDialog, items } = this;
const { selectedItems } = store; const { selectedItems } = store;
@ -426,6 +426,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
)} )}
{isReady && ( {isReady && (
<Table <Table
tableId={tableId}
virtual={virtual} virtual={virtual}
selectable={hasDetailsView} selectable={hasDetailsView}
sortable={sortingCallbacks} sortable={sortingCallbacks}

View File

@ -0,0 +1,22 @@
import { createStorage } from "../../utils";
import { TableSortParams } from "./table";
export interface TableStorageModel {
sortParams: {
[tableId: string]: Partial<TableSortParams>;
}
}
export const tableStorage = createStorage<TableStorageModel>("table_settings", {
sortParams: {}
});
export function getSortParams(tableId: string): Partial<TableSortParams> {
return tableStorage.get().sortParams[tableId];
}
export function setSortParams(tableId: string, sortParams: Partial<TableSortParams>) {
tableStorage.merge(draft => {
draft.sortParams[tableId] = sortParams;
});
}

View File

@ -3,7 +3,6 @@ import "./table.scss";
import React from "react"; import React from "react";
import { orderBy } from "lodash"; import { orderBy } from "lodash";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { observable } from "mobx";
import { autobind, cssNames, noop } from "../../utils"; import { autobind, cssNames, noop } from "../../utils";
import { TableRow, TableRowElem, TableRowProps } from "./table-row"; import { TableRow, TableRowElem, TableRowProps } from "./table-row";
import { TableHead, TableHeadElem, TableHeadProps } from "./table-head"; import { TableHead, TableHeadElem, TableHeadProps } from "./table-head";
@ -11,6 +10,8 @@ import { TableCellElem } from "./table-cell";
import { VirtualList } from "../virtual-list"; import { VirtualList } from "../virtual-list";
import { createPageParam } from "../../navigation"; import { createPageParam } from "../../navigation";
import { ItemObject } from "../../item.store"; import { ItemObject } from "../../item.store";
import { getSortParams, setSortParams } from "./table.storage";
import { computed } from "mobx";
export type TableSortBy = string; export type TableSortBy = string;
export type TableOrderBy = "asc" | "desc" | string; export type TableOrderBy = "asc" | "desc" | string;
@ -19,6 +20,7 @@ export type TableSortCallback<D = any> = (data: D) => string | number | (string
export type TableSortCallbacks = { [columnId: string]: TableSortCallback }; export type TableSortCallbacks = { [columnId: string]: TableSortCallback };
export interface TableProps extends React.DOMAttributes<HTMLDivElement> { export interface TableProps extends React.DOMAttributes<HTMLDivElement> {
tableId?: string;
items?: ItemObject[]; // Raw items data items?: ItemObject[]; // 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)
@ -62,13 +64,17 @@ export class Table extends React.Component<TableProps> {
sortSyncWithUrl: true, sortSyncWithUrl: true,
}; };
@observable sortParams: Partial<TableSortParams> = Object.assign( componentDidMount() {
this.props.sortSyncWithUrl ? { const { sortable, tableId } = this.props;
sortBy: sortByUrlParam.get(),
orderBy: orderByUrlParam.get(), if (sortable && !tableId) {
} : {}, console.error("[Table]: sorted table requires props.tableId to be specified");
this.props.sortByDefault, }
); }
@computed get sortParams() {
return Object.assign({}, this.props.sortByDefault, getSortParams(this.props.tableId));
}
renderHead() { renderHead() {
const { sortable, children } = this.props; const { sortable, children } = this.props;
@ -113,7 +119,7 @@ export class Table extends React.Component<TableProps> {
@autobind() @autobind()
protected onSort({ sortBy, orderBy }: TableSortParams) { protected onSort({ sortBy, orderBy }: TableSortParams) {
this.sortParams = { sortBy, orderBy }; setSortParams(this.props.tableId, { sortBy, orderBy });
const { sortSyncWithUrl, onSort } = this.props; const { sortSyncWithUrl, onSort } = this.props;
if (sortSyncWithUrl) { if (sortSyncWithUrl) {

View File

@ -2,7 +2,7 @@ import { useState } from "react";
import { createStorage } from "../utils"; import { createStorage } from "../utils";
import { CreateObservableOptions } from "mobx/lib/api/observable"; import { CreateObservableOptions } from "mobx/lib/api/observable";
export function useStorage<T>(key: string, initialValue?: T, options?: CreateObservableOptions) { export function useStorage<T>(key: string, initialValue: T, options?: CreateObservableOptions) {
const storage = createStorage(key, initialValue, options); const storage = createStorage(key, initialValue, options);
const [storageValue, setStorageValue] = useState(storage.get()); const [storageValue, setStorageValue] = useState(storage.get());
const setValue = (value: T) => { const setValue = (value: T) => {

View File

@ -142,17 +142,6 @@ describe("renderer/utils/StorageHelper", () => {
})); }));
expect(storageHelper.get()).toEqual({ ...storageHelperDefaultValue, message: "updated3" }); expect(storageHelper.get()).toEqual({ ...storageHelperDefaultValue, message: "updated3" });
}); });
it("clears data in storage", () => {
storageHelper.init();
expect(storageHelper.get()).toBeTruthy();
storageHelper.clear();
expect(storageHelper.get()).toBeFalsy();
expect(storageMock[storageKey]).toBeUndefined();
expect(storageAdapter.removeItem).toHaveBeenCalledWith(storageHelper.key);
});
}); });
describe("data in storage-helper is observable (mobx)", () => { describe("data in storage-helper is observable (mobx)", () => {

View File

@ -14,7 +14,7 @@ let initialized = false;
const loaded = observable.box(false); const loaded = observable.box(false);
const storage = observable.map<string/* key */, any /* serializable */>(); const storage = observable.map<string/* key */, any /* serializable */>();
export function createStorage<T>(key: string, defaultValue?: T, observableOptions?: CreateObservableOptions) { export function createStorage<T>(key: string, defaultValue: T, observableOptions?: CreateObservableOptions) {
const clusterId = getHostedClusterId(); const clusterId = getHostedClusterId();
const savingFolder = path.resolve((app || remote.app).getPath("userData"), "lens-local-storage"); const savingFolder = path.resolve((app || remote.app).getPath("userData"), "lens-local-storage");
const jsonFilePath = path.resolve(savingFolder, `${clusterId ?? "app"}.json`); const jsonFilePath = path.resolve(savingFolder, `${clusterId ?? "app"}.json`);

View File

@ -21,7 +21,7 @@ export interface StorageHelperOptions<T> {
autoInit?: boolean; // start preloading data immediately, default: true autoInit?: boolean; // start preloading data immediately, default: true
observable?: CreateObservableOptions; observable?: CreateObservableOptions;
storage: StorageAdapter<T>; storage: StorageAdapter<T>;
defaultValue?: T; defaultValue: T;
} }
export class StorageHelper<T> { export class StorageHelper<T> {
@ -133,17 +133,18 @@ export class StorageHelper<T> {
} }
set(value: T) { set(value: T) {
this.data.set(value); if (value == null) {
// This cannot use recursion because defaultValue might be null or undefined
this.data.set(this.defaultValue);
} else {
this.data.set(value);
}
} }
reset() { reset() {
this.set(this.defaultValue); this.set(this.defaultValue);
} }
clear() {
this.data.set(null);
}
merge(value: Partial<T> | ((draft: Draft<T>) => Partial<T> | void)) { merge(value: Partial<T> | ((draft: Draft<T>) => Partial<T> | void)) {
const nextValue = produce(this.get(), (state: Draft<T>) => { const nextValue = produce(this.get(), (state: Draft<T>) => {
const newValue = isFunction(value) ? value(state) : value; const newValue = isFunction(value) ? value(state) : value;