mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Fix NamespaceSelectFilter to correctly track accessibleNamespaces
- Consolidate the ClusterContext functions into a class - Add method for the correct determiation of whether all possible namespaces are selected to ClusterContext - Use said method in the two API related use cases as well in the above component Fix only able to select one namespace - Extends isAllPossibleNamespaces with isFilterSelect option to correctly check the empty namespaces imply selecting all case - renamed contextNamespaces to selectedNamespaces - renamed contextNs to rawSelectedNamespaces fix not restarting subscribes when adding new accessibleNamespaces Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
53606202c1
commit
df7f72e84c
8
src/common/utils/as-tuple.ts
Normal file
8
src/common/utils/as-tuple.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* This function changes the TS type from an array literal over the union of
|
||||||
|
* element types to a strict tuple.
|
||||||
|
* @param arg The array literal to be made into a tuple
|
||||||
|
*/
|
||||||
|
export function asTuple<T extends [any] | any[]>(arg: T): T {
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@
|
|||||||
export const noop: any = () => { /* empty */ };
|
export const noop: any = () => { /* empty */ };
|
||||||
|
|
||||||
export * from "./app-version";
|
export * from "./app-version";
|
||||||
|
export * from "./as-tuple";
|
||||||
export * from "./autobind";
|
export * from "./autobind";
|
||||||
export * from "./base64";
|
export * from "./base64";
|
||||||
export * from "./camelCase";
|
export * from "./camelCase";
|
||||||
@ -11,6 +12,7 @@ export * from "./debouncePromise";
|
|||||||
export * from "./defineGlobal";
|
export * from "./defineGlobal";
|
||||||
export * from "./delay";
|
export * from "./delay";
|
||||||
export * from "./disposer";
|
export * from "./disposer";
|
||||||
|
export * from "./disposer";
|
||||||
export * from "./downloadFile";
|
export * from "./downloadFile";
|
||||||
export * from "./escapeRegExp";
|
export * from "./escapeRegExp";
|
||||||
export * from "./getRandId";
|
export * from "./getRandId";
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import type { ClusterContext } from "../components/context";
|
|||||||
|
|
||||||
import plimit from "p-limit";
|
import plimit from "p-limit";
|
||||||
import { comparer, IReactionDisposer, observable, reaction, when } from "mobx";
|
import { comparer, IReactionDisposer, observable, reaction, when } from "mobx";
|
||||||
import { autobind, noop } from "../utils";
|
import { asTuple, autobind, noop } from "../utils";
|
||||||
import { KubeApi } from "./kube-api";
|
import { KubeApi } from "./kube-api";
|
||||||
import { KubeJsonApiData } from "./kube-json-api";
|
import { KubeJsonApiData } from "./kube-json-api";
|
||||||
import { isDebugging, isProduction } from "../../common/vars";
|
import { isDebugging, isProduction } from "../../common/vars";
|
||||||
@ -88,7 +88,15 @@ export class KubeWatchApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reload stores only for context namespaces change
|
// reload stores only for context namespaces change
|
||||||
cancelReloading = reaction(() => this.context?.contextNamespaces, namespaces => {
|
cancelReloading = reaction(() => {
|
||||||
|
const namespaces = this.context?.selectedNamespaces;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* react to the changing of "allPossibleNamespaces" so that adding
|
||||||
|
* accessibleNamespaces means that this is restarted
|
||||||
|
*/
|
||||||
|
return asTuple([this.context?.isAllPossibleNamespaces(namespaces), namespaces]);
|
||||||
|
}, ([, namespaces]) => {
|
||||||
preloading?.cancelLoading();
|
preloading?.cancelLoading();
|
||||||
unsubscribeList.forEach(unsubscribe => unsubscribe());
|
unsubscribeList.forEach(unsubscribe => unsubscribe());
|
||||||
unsubscribeList.length = 0;
|
unsubscribeList.length = 0;
|
||||||
|
|||||||
@ -7,11 +7,19 @@ import { Secret } from "../../api/endpoints";
|
|||||||
import { secretsStore } from "../+config-secrets/secrets.store";
|
import { secretsStore } from "../+config-secrets/secrets.store";
|
||||||
import { namespaceStore } from "../+namespaces/namespace.store";
|
import { namespaceStore } from "../+namespaces/namespace.store";
|
||||||
import { Notifications } from "../notifications";
|
import { Notifications } from "../notifications";
|
||||||
|
import { ClusterContext } from "../context";
|
||||||
|
|
||||||
@autobind()
|
@autobind()
|
||||||
export class ReleaseStore extends ItemStore<HelmRelease> {
|
export class ReleaseStore extends ItemStore<HelmRelease> {
|
||||||
|
@observable static defaultContext: ClusterContext; // TODO: support multiple cluster contexts
|
||||||
releaseSecrets = observable.map<string, Secret>();
|
releaseSecrets = observable.map<string, Secret>();
|
||||||
|
|
||||||
|
contextReady = when(() => Boolean(this.context));
|
||||||
|
|
||||||
|
get context(): ClusterContext {
|
||||||
|
return ReleaseStore.defaultContext;
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
when(() => secretsStore.isLoaded, () => {
|
when(() => secretsStore.isLoaded, () => {
|
||||||
@ -36,7 +44,7 @@ export class ReleaseStore extends ItemStore<HelmRelease> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watchSelecteNamespaces(): (() => void) {
|
watchSelecteNamespaces(): (() => void) {
|
||||||
return reaction(() => namespaceStore.context.contextNamespaces, namespaces => {
|
return reaction(() => namespaceStore.selectedNamespaces, namespaces => {
|
||||||
this.loadAll(namespaces);
|
this.loadAll(namespaces);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -79,15 +87,13 @@ export class ReleaseStore extends ItemStore<HelmRelease> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadFromContextNamespaces(): Promise<void> {
|
async loadFromContextNamespaces(): Promise<void> {
|
||||||
return this.loadAll(namespaceStore.context.contextNamespaces);
|
return this.loadAll(namespaceStore.selectedNamespaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadItems(namespaces: string[]) {
|
async loadItems(namespaces: string[]) {
|
||||||
const isLoadingAll = namespaceStore.context.allNamespaces?.length > 1
|
await this.contextReady;
|
||||||
&& namespaceStore.context.cluster.accessibleNamespaces.length === 0
|
|
||||||
&& namespaceStore.context.allNamespaces.every(ns => namespaces.includes(ns));
|
|
||||||
|
|
||||||
if (isLoadingAll) {
|
if (this.context.isAllPossibleNamespaces(namespaces)) {
|
||||||
return listReleases();
|
return listReleases();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,11 +13,14 @@ import { namespaceStore } from "./namespace.store";
|
|||||||
|
|
||||||
const Placeholder = observer((props: PlaceholderProps<any>) => {
|
const Placeholder = observer((props: PlaceholderProps<any>) => {
|
||||||
const getPlaceholder = (): React.ReactNode => {
|
const getPlaceholder = (): React.ReactNode => {
|
||||||
const namespaces = namespaceStore.contextNamespaces;
|
const namespaces = namespaceStore.selectedNamespaces;
|
||||||
|
|
||||||
|
if (namespaceStore.selectedAll) {
|
||||||
|
return <>All namespaces</>;
|
||||||
|
}
|
||||||
|
|
||||||
switch (namespaces.length) {
|
switch (namespaces.length) {
|
||||||
case 0:
|
case 0:
|
||||||
case namespaceStore.allowedNamespaces.length:
|
|
||||||
return <>All namespaces</>;
|
return <>All namespaces</>;
|
||||||
case 1:
|
case 1:
|
||||||
return <>Namespace: {namespaces[0]}</>;
|
return <>Namespace: {namespaces[0]}</>;
|
||||||
|
|||||||
@ -11,17 +11,32 @@ import { kubeWatchApi } from "../../api/kube-watch-api";
|
|||||||
import { components, ValueContainerProps } from "react-select";
|
import { components, ValueContainerProps } from "react-select";
|
||||||
|
|
||||||
interface Props extends SelectProps {
|
interface Props extends SelectProps {
|
||||||
|
/**
|
||||||
|
* Show icons preceeding the entry names
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
showIcons?: boolean;
|
showIcons?: boolean;
|
||||||
showClusterOption?: boolean; // show "Cluster" option on the top (default: false)
|
|
||||||
showAllNamespacesOption?: boolean; // show "All namespaces" option on the top (default: false)
|
/**
|
||||||
|
* show a "Cluster" option above all namespaces
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
showClusterOption?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* show "All namespaces" option on the top (has precedence over `showClusterOption`)
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
showAllNamespacesOption?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function to change the options for the select
|
||||||
|
* @param options the current options to display
|
||||||
|
* @default passthrough
|
||||||
|
*/
|
||||||
customizeOptions?(options: SelectOption[]): SelectOption[];
|
customizeOptions?(options: SelectOption[]): SelectOption[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultProps: Partial<Props> = {
|
|
||||||
showIcons: true,
|
|
||||||
showClusterOption: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
function GradientValueContainer<T>({children, ...rest}: ValueContainerProps<T>) {
|
function GradientValueContainer<T>({children, ...rest}: ValueContainerProps<T>) {
|
||||||
return (
|
return (
|
||||||
<components.ValueContainer {...rest}>
|
<components.ValueContainer {...rest}>
|
||||||
@ -34,7 +49,12 @@ function GradientValueContainer<T>({children, ...rest}: ValueContainerProps<T>)
|
|||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class NamespaceSelect extends React.Component<Props> {
|
export class NamespaceSelect extends React.Component<Props> {
|
||||||
static defaultProps = defaultProps as object;
|
static defaultProps: Props = {
|
||||||
|
showIcons: true,
|
||||||
|
showClusterOption: false,
|
||||||
|
showAllNamespacesOption: false,
|
||||||
|
customizeOptions: (opts) => opts,
|
||||||
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
disposeOnUnmount(this, [
|
disposeOnUnmount(this, [
|
||||||
@ -47,7 +67,7 @@ export class NamespaceSelect extends React.Component<Props> {
|
|||||||
|
|
||||||
@computed.struct get options(): SelectOption[] {
|
@computed.struct get options(): SelectOption[] {
|
||||||
const { customizeOptions, showClusterOption, showAllNamespacesOption } = this.props;
|
const { customizeOptions, showClusterOption, showAllNamespacesOption } = this.props;
|
||||||
let options: SelectOption[] = namespaceStore.items.map(ns => ({ value: ns.getName() }));
|
const options: SelectOption[] = namespaceStore.allowedNamespaces.map(ns => ({ value: ns }));
|
||||||
|
|
||||||
if (showAllNamespacesOption) {
|
if (showAllNamespacesOption) {
|
||||||
options.unshift({ label: "All Namespaces", value: "" });
|
options.unshift({ label: "All Namespaces", value: "" });
|
||||||
@ -55,11 +75,7 @@ export class NamespaceSelect extends React.Component<Props> {
|
|||||||
options.unshift({ label: "Cluster", value: "" });
|
options.unshift({ label: "Cluster", value: "" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (customizeOptions) {
|
return customizeOptions(options);
|
||||||
options = customizeOptions(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
formatOptionLabel = (option: SelectOption) => {
|
formatOptionLabel = (option: SelectOption) => {
|
||||||
|
|||||||
@ -31,7 +31,14 @@ export function getDummyNamespace(name: string) {
|
|||||||
export class NamespaceStore extends KubeObjectStore<Namespace> {
|
export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||||
api = namespacesApi;
|
api = namespacesApi;
|
||||||
|
|
||||||
@observable private contextNs = observable.set<string>();
|
@observable private rawSelectedNamespaces = observable.set<string>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depreated use `NamespaceStore.rawSelectedNamespaces` instead
|
||||||
|
*/
|
||||||
|
get contextNs() {
|
||||||
|
return this.rawSelectedNamespaces;
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@ -48,7 +55,7 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onContextChange(callback: (contextNamespaces: string[]) => void, opts: IReactionOptions = {}): IReactionDisposer {
|
public onContextChange(callback: (contextNamespaces: string[]) => void, opts: IReactionOptions = {}): IReactionDisposer {
|
||||||
return reaction(() => Array.from(this.contextNs), callback, {
|
return reaction(() => Array.from(this.rawSelectedNamespaces), callback, {
|
||||||
equals: comparer.shallow,
|
equals: comparer.shallow,
|
||||||
...opts,
|
...opts,
|
||||||
});
|
});
|
||||||
@ -89,6 +96,16 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use `NamespaceStore.allowedNamespaces` instead
|
||||||
|
*/
|
||||||
|
get contextNamespaces() {
|
||||||
|
return this.allowedNamespaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The array of namespace names that this store knows about
|
||||||
|
*/
|
||||||
@computed get allowedNamespaces(): string[] {
|
@computed get allowedNamespaces(): string[] {
|
||||||
return Array.from(new Set([
|
return Array.from(new Set([
|
||||||
...(this.context?.allNamespaces ?? []), // allowed namespaces from cluster (main), updating every 30s
|
...(this.context?.allNamespaces ?? []), // allowed namespaces from cluster (main), updating every 30s
|
||||||
@ -96,8 +113,8 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
|||||||
].flat()));
|
].flat()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get contextNamespaces(): string[] {
|
@computed get selectedNamespaces(): string[] {
|
||||||
const namespaces = Array.from(this.contextNs);
|
const namespaces = Array.from(this.rawSelectedNamespaces);
|
||||||
|
|
||||||
if (!namespaces.length) {
|
if (!namespaces.length) {
|
||||||
return this.allowedNamespaces; // show all namespaces when nothing selected
|
return this.allowedNamespaces; // show all namespaces when nothing selected
|
||||||
@ -133,28 +150,27 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
|||||||
setContext(namespace: string | string[]) {
|
setContext(namespace: string | string[]) {
|
||||||
const namespaces = [namespace].flat();
|
const namespaces = [namespace].flat();
|
||||||
|
|
||||||
this.contextNs.replace(namespaces);
|
this.rawSelectedNamespaces.replace(namespaces);
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
resetContext() {
|
|
||||||
this.contextNs.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hasContext(namespaces: string | string[]) {
|
hasContext(namespaces: string | string[]) {
|
||||||
return [namespaces].flat().every(namespace => this.contextNs.has(namespace));
|
return [namespaces].flat().every(namespace => this.rawSelectedNamespaces.has(namespace));
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get selectedAll(): boolean {
|
||||||
|
return this.context?.isAllPossibleNamespaces(Array.from(this.rawSelectedNamespaces), true) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get hasAllContexts(): boolean {
|
@computed get hasAllContexts(): boolean {
|
||||||
return this.contextNs.size === this.allowedNamespaces.length;
|
return this.rawSelectedNamespaces.size === this.allowedNamespaces.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
toggleContext(namespace: string) {
|
toggleContext(namespace: string) {
|
||||||
if (this.hasContext(namespace)) {
|
if (this.hasContext(namespace)) {
|
||||||
this.contextNs.delete(namespace);
|
this.rawSelectedNamespaces.delete(namespace);
|
||||||
} else {
|
} else {
|
||||||
this.contextNs.add(namespace);
|
this.rawSelectedNamespaces.add(namespace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +180,7 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
|||||||
if (showAll) {
|
if (showAll) {
|
||||||
this.setContext(this.allowedNamespaces);
|
this.setContext(this.allowedNamespaces);
|
||||||
} else {
|
} else {
|
||||||
this.resetContext(); // empty context considered as "All namespaces"
|
this.rawSelectedNamespaces.clear();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.toggleAll(!this.hasAllContexts);
|
this.toggleAll(!this.hasAllContexts);
|
||||||
@ -174,7 +190,7 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
|||||||
@action
|
@action
|
||||||
async remove(item: Namespace) {
|
async remove(item: Namespace) {
|
||||||
await super.remove(item);
|
await super.remove(item);
|
||||||
this.contextNs.delete(item.getName());
|
this.rawSelectedNamespaces.delete(item.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export class OverviewStatuses extends React.Component {
|
|||||||
@autobind()
|
@autobind()
|
||||||
renderWorkload(resource: KubeResource): React.ReactElement {
|
renderWorkload(resource: KubeResource): React.ReactElement {
|
||||||
const store = workloadStores[resource];
|
const store = workloadStores[resource];
|
||||||
const items = store.getAllByNs(namespaceStore.contextNamespaces);
|
const items = store.getAllByNs(namespaceStore.selectedNamespaces);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="workload" key={resource}>
|
<div className="workload" key={resource}>
|
||||||
|
|||||||
@ -16,13 +16,16 @@ import { cronJobStore } from "../+workloads-cronjobs/cronjob.store";
|
|||||||
import { Events } from "../+events";
|
import { Events } from "../+events";
|
||||||
import { isAllowedResource } from "../../../common/rbac";
|
import { isAllowedResource } from "../../../common/rbac";
|
||||||
import { kubeWatchApi } from "../../api/kube-watch-api";
|
import { kubeWatchApi } from "../../api/kube-watch-api";
|
||||||
import { clusterContext } from "../context";
|
import { observable } from "mobx";
|
||||||
|
import { ClusterContext } from "../context";
|
||||||
|
|
||||||
interface Props extends RouteComponentProps<IWorkloadsOverviewRouteParams> {
|
interface Props extends RouteComponentProps<IWorkloadsOverviewRouteParams> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class WorkloadsOverview extends React.Component<Props> {
|
export class WorkloadsOverview extends React.Component<Props> {
|
||||||
|
@observable static defaultContext: ClusterContext; // TODO: support multiple cluster contexts
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
disposeOnUnmount(this, [
|
disposeOnUnmount(this, [
|
||||||
kubeWatchApi.subscribeStores([
|
kubeWatchApi.subscribeStores([
|
||||||
@ -30,7 +33,7 @@ export class WorkloadsOverview extends React.Component<Props> {
|
|||||||
jobStore, cronJobStore, eventStore,
|
jobStore, cronJobStore, eventStore,
|
||||||
], {
|
], {
|
||||||
preload: true,
|
preload: true,
|
||||||
namespaces: clusterContext.contextNamespaces,
|
namespaces: WorkloadsOverview.defaultContext.selectedNamespaces,
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,9 @@ import { kubeWatchApi } from "../api/kube-watch-api";
|
|||||||
import { ReplicaSetScaleDialog } from "./+workloads-replicasets/replicaset-scale-dialog";
|
import { ReplicaSetScaleDialog } from "./+workloads-replicasets/replicaset-scale-dialog";
|
||||||
import { CommandContainer } from "./command-palette/command-container";
|
import { CommandContainer } from "./command-palette/command-container";
|
||||||
import { KubeObjectStore } from "../kube-object.store";
|
import { KubeObjectStore } from "../kube-object.store";
|
||||||
import { clusterContext } from "./context";
|
import { ReleaseStore } from "./+apps-releases/release.store";
|
||||||
|
import { ClusterContext } from "./context";
|
||||||
|
import { WorkloadsOverview } from "./+workloads-overview/overview";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class App extends React.Component {
|
export class App extends React.Component {
|
||||||
@ -78,8 +80,11 @@ export class App extends React.Component {
|
|||||||
whatInput.ask(); // Start to monitor user input device
|
whatInput.ask(); // Start to monitor user input device
|
||||||
|
|
||||||
// Setup hosted cluster context
|
// Setup hosted cluster context
|
||||||
KubeObjectStore.defaultContext = clusterContext;
|
KubeObjectStore.defaultContext
|
||||||
kubeWatchApi.context = clusterContext;
|
= ReleaseStore.defaultContext
|
||||||
|
= WorkloadsOverview.defaultContext
|
||||||
|
= kubeWatchApi.context
|
||||||
|
= new ClusterContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
|||||||
@ -2,16 +2,10 @@ import type { Cluster } from "../../main/cluster";
|
|||||||
import { getHostedCluster } from "../../common/cluster-store";
|
import { getHostedCluster } from "../../common/cluster-store";
|
||||||
import { namespaceStore } from "./+namespaces/namespace.store";
|
import { namespaceStore } from "./+namespaces/namespace.store";
|
||||||
|
|
||||||
export interface ClusterContext {
|
export class ClusterContext {
|
||||||
cluster?: Cluster;
|
|
||||||
allNamespaces: string[]; // available / allowed namespaces from cluster.ts
|
|
||||||
contextNamespaces: string[]; // selected by user (see: namespace-select.tsx)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const clusterContext: ClusterContext = {
|
|
||||||
get cluster(): Cluster | null {
|
get cluster(): Cluster | null {
|
||||||
return getHostedCluster();
|
return getHostedCluster() ?? null;
|
||||||
},
|
}
|
||||||
|
|
||||||
get allNamespaces(): string[] {
|
get allNamespaces(): string[] {
|
||||||
if (!this.cluster) {
|
if (!this.cluster) {
|
||||||
@ -30,9 +24,25 @@ export const clusterContext: ClusterContext = {
|
|||||||
// fallback to cluster resolved namespaces because we could not load list
|
// fallback to cluster resolved namespaces because we could not load list
|
||||||
return this.cluster.allowedNamespaces || [];
|
return this.cluster.allowedNamespaces || [];
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
get contextNamespaces(): string[] {
|
/**
|
||||||
return namespaceStore.contextNamespaces ?? [];
|
* This function returns true if the list of namespaces provided is the
|
||||||
},
|
* same as all the namespaces that exist (for certain) on the cluster
|
||||||
};
|
* @param namespaces The list of namespaces to check
|
||||||
|
*/
|
||||||
|
public isAllPossibleNamespaces(namespaceList: string[], isFilterSelect = false): boolean {
|
||||||
|
const namespaces = new Set(namespaceList);
|
||||||
|
|
||||||
|
return this.allNamespaces.length > 1
|
||||||
|
&& this.cluster.accessibleNamespaces.length === 0
|
||||||
|
&& (
|
||||||
|
(isFilterSelect && namespaces.size === 0)
|
||||||
|
|| this.allNamespaces.every(ns => namespaces.has(ns))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get selectedNamespaces(): string[] {
|
||||||
|
return namespaceStore.selectedNamespaces ?? [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -137,7 +137,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
|||||||
const stores = Array.from(new Set([store, ...dependentStores]));
|
const stores = Array.from(new Set([store, ...dependentStores]));
|
||||||
|
|
||||||
// load context namespaces by default (see also: `<NamespaceSelectFilter/>`)
|
// load context namespaces by default (see also: `<NamespaceSelectFilter/>`)
|
||||||
stores.forEach(store => store.loadAll(namespaceStore.contextNamespaces));
|
stores.forEach(store => store.loadAll(namespaceStore.selectedNamespaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
private filterCallbacks: { [type: string]: ItemsFilter } = {
|
private filterCallbacks: { [type: string]: ItemsFilter } = {
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import { KubeObjectStore } from "../../kube-object.store";
|
|||||||
import { KubeObjectMenu } from "./kube-object-menu";
|
import { KubeObjectMenu } from "./kube-object-menu";
|
||||||
import { kubeSelectedUrlParam, showDetails } from "./kube-object-details";
|
import { kubeSelectedUrlParam, showDetails } from "./kube-object-details";
|
||||||
import { kubeWatchApi } from "../../api/kube-watch-api";
|
import { kubeWatchApi } from "../../api/kube-watch-api";
|
||||||
import { clusterContext } from "../context";
|
|
||||||
|
|
||||||
export interface KubeObjectListLayoutProps extends ItemListLayoutProps {
|
export interface KubeObjectListLayoutProps extends ItemListLayoutProps {
|
||||||
store: KubeObjectStore;
|
store: KubeObjectStore;
|
||||||
@ -34,7 +33,7 @@ export class KubeObjectListLayout extends React.Component<KubeObjectListLayoutPr
|
|||||||
disposeOnUnmount(this, [
|
disposeOnUnmount(this, [
|
||||||
kubeWatchApi.subscribeStores(stores, {
|
kubeWatchApi.subscribeStores(stores, {
|
||||||
preload: true,
|
preload: true,
|
||||||
namespaces: clusterContext.contextNamespaces,
|
namespaces: store.context.selectedNamespaces,
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
@computed get contextItems(): T[] {
|
@computed get contextItems(): T[] {
|
||||||
const namespaces = this.context?.contextNamespaces ?? [];
|
const namespaces = this.context?.selectedNamespaces ?? [];
|
||||||
|
|
||||||
return this.items.filter(item => {
|
return this.items.filter(item => {
|
||||||
const itemNamespace = item.getNs();
|
const itemNamespace = item.getNs();
|
||||||
@ -109,11 +109,7 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
|
|||||||
return api.list({}, this.query);
|
return api.list({}, this.query);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLoadingAll = this.context.allNamespaces?.length > 1
|
if (this.context.isAllPossibleNamespaces(namespaces)) {
|
||||||
&& this.context.cluster.accessibleNamespaces.length === 0
|
|
||||||
&& this.context.allNamespaces.every(ns => namespaces.includes(ns));
|
|
||||||
|
|
||||||
if (isLoadingAll) {
|
|
||||||
this.loadedNamespaces = [];
|
this.loadedNamespaces = [];
|
||||||
|
|
||||||
return api.list({}, this.query);
|
return api.list({}, this.query);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user