Watch-api streaming reworks (#1990)

* loading k8s resources into stores per selected namespaces -- part 1

Signed-off-by: Roman <ixrock@gmail.com>

* loading k8s resources into stores per selected namespaces -- part 2
- fix: generating helm chart id

Signed-off-by: Roman <ixrock@gmail.com>

* loading k8s resources into stores per selected namespaces -- part 3

Signed-off-by: Roman <ixrock@gmail.com>

* fixes

Signed-off-by: Roman <ixrock@gmail.com>

* fixes / responding to comments

Signed-off-by: Roman <ixrock@gmail.com>

* chore / small fixes

Signed-off-by: Roman <ixrock@gmail.com>

* Watch api does not work for non-admins with lots of namespaces #1898 -- part 1

Signed-off-by: Roman <ixrock@gmail.com>

* fixes & refactoring

Signed-off-by: Roman <ixrock@gmail.com>

* make lint happy

Signed-off-by: Roman <ixrock@gmail.com>

* reset store on loading error

Signed-off-by: Roman <ixrock@gmail.com>

* added new cluster method: cluster.isAllowedResource

Signed-off-by: Roman <ixrock@gmail.com>

* fix: loading namespaces optimizations

Signed-off-by: Roman <ixrock@gmail.com>

* fixes & refactoring

Signed-off-by: Roman <ixrock@gmail.com>

* fix: parse multiple kube-events from stream's chunk

Signed-off-by: Roman <ixrock@gmail.com>

* fix: mobx issue with accessing empty observable array by index (removes warning), use common logger

Signed-off-by: Roman <ixrock@gmail.com>

* fine-tuning

Signed-off-by: Roman <ixrock@gmail.com>

* fix: parse json stream chunks at client-side (might be partial, depends on network speed)

Signed-off-by: Roman <ixrock@gmail.com>

* store subscribing refactoring -- part 1

Signed-off-by: Roman <ixrock@gmail.com>

* store subscribing refactoring -- part 2

Signed-off-by: Roman <ixrock@gmail.com>

* store subscribing refactoring -- part 3

Signed-off-by: Roman <ixrock@gmail.com>

* store subscribing refactoring -- part 4

Signed-off-by: Roman <ixrock@gmail.com>

* auto-reconnect on online/offline status change, interval connection check

Signed-off-by: Roman <ixrock@gmail.com>

* check connection every 5m

Signed-off-by: Roman <ixrock@gmail.com>

* split concurrent watch-api requests by 10 at a time + 150ms delay before next call

Signed-off-by: Roman <ixrock@gmail.com>

* refactoring / clean up

Signed-off-by: Roman <ixrock@gmail.com>

* use `plimit` + delay for k8s watch requests

Signed-off-by: Roman <ixrock@gmail.com>

* lint fix

Signed-off-by: Roman <ixrock@gmail.com>

* added explicit `preload: true` when subscribing stores

Signed-off-by: Roman <ixrock@gmail.com>

* kubeWatchApi refactoring / fine-tuning

Signed-off-by: Roman <ixrock@gmail.com>

* clean up

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman authored and GitHub committed 2021-02-01 15:49:32 +02:00
1 parent 7490b15aad
commit 078f952b36
20 files changed
+519 -395

No files matched your search

@@ -2,7 +2,7 @@ import "./item-list-layout.scss";
import groupBy from "lodash/groupBy";
import React, { ReactNode } from "react";
import { computed, IReactionDisposer, observable, reaction, toJS } from "mobx";
import { computed, observable, reaction, toJS } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { ConfirmDialog, ConfirmDialogParams } from "../confirm-dialog";
import { Table, TableCell, TableCellProps, TableHead, TableProps, TableRow, TableRowProps, TableSortCallback } from "../table";
@@ -12,7 +12,6 @@ import { NoItems } from "../no-items";
import { Spinner } from "../spinner";
import { ItemObject, ItemStore } from "../../item.store";
import { SearchInputUrl } from "../input";
import { namespaceStore } from "../+namespaces/namespace.store";
import { Filter, FilterType, pageFilters } from "./page-filters.store";
import { PageFiltersList } from "./page-filters-list";
import { PageFiltersSelect } from "./page-filters-select";
@@ -22,6 +21,7 @@ import { MenuActions } from "../menu/menu-actions";
import { MenuItem } from "../menu";
import { Checkbox } from "../checkbox";
import { userStore } from "../../../common/user-store";
import { namespaceStore } from "../+namespaces/namespace.store";
// todo: refactor, split to small re-usable components
@@ -40,6 +40,7 @@ export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
className: IClassName;
store: ItemStore<T>;
dependentStores?: ItemStore[];
preloadStores?: boolean;
isClusterScoped?: boolean;
hideFilters?: boolean;
searchFilters?: SearchFilter<T>[];
@@ -82,6 +83,7 @@ const defaultProps: Partial<ItemListLayoutProps> = {
isSelectable: true,
isConfigurable: false,
copyClassNameFromHeadCells: true,
preloadStores: true,
dependentStores: [],
filterItems: [],
hasDetailsView: true,
@@ -97,10 +99,6 @@ interface ItemListLayoutUserSettings {
export class ItemListLayout extends React.Component<ItemListLayoutProps> {
static defaultProps = defaultProps as object;
private watchDisposers: IReactionDisposer[] = [];
@observable isUnmounting = false;
@observable userSettings: ItemListLayoutUserSettings = {
showAppliedFilters: false,
};
@@ -119,54 +117,28 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
}
async componentDidMount() {
const { isClusterScoped, isConfigurable, tableId } = this.props;
const { isClusterScoped, isConfigurable, tableId, preloadStores } = this.props;
if (isConfigurable && !tableId) {
throw new Error("[ItemListLayout]: configurable list require props.tableId to be specified");
}
this.loadStores();
if (preloadStores) {
this.loadStores();
if (!isClusterScoped) {
disposeOnUnmount(this, [
namespaceStore.onContextChange(() => this.loadStores())
]);
if (!isClusterScoped) {
disposeOnUnmount(this, [
namespaceStore.onContextChange(() => this.loadStores())
]);
}
}
}
async componentWillUnmount() {
this.isUnmounting = true;
this.unsubscribeStores();
}
@computed get stores() {
private loadStores() {
const { store, dependentStores } = this.props;
const stores = Array.from(new Set([store, ...dependentStores]));
return new Set([store, ...dependentStores]);
}
async loadStores() {
this.unsubscribeStores(); // reset first
// load
for (const store of this.stores) {
if (this.isUnmounting) {
this.unsubscribeStores();
break;
}
try {
await store.loadAll();
this.watchDisposers.push(store.subscribe());
} catch (error) {
console.error("loading store error", error);
}
}
}
unsubscribeStores() {
this.watchDisposers.forEach(dispose => dispose());
this.watchDisposers.length = 0;
stores.forEach(store => store.loadAll());
}
private filterCallbacks: { [type: string]: ItemsFilter } = {