From 4ec8d230db48c4ce973f89ab98b0de27c57fa387 Mon Sep 17 00:00:00 2001
From: Paul Richardson
Date: Mon, 23 Sep 2024 15:56:14 +0100
Subject: [PATCH] Change filtering to push down into the mgmt and k8 services
(#496)
* namespace-client.ts
* Change name of 2nd callback parameter to fullPodCount to make it clear
this is the total jolokia pods after filtering but before page slicing
* Adds filtering and sorting getters/setters to affect the pods returned
by the pod watchers
* If any change occurs from filtering or sorting then the callback should
be called in order to notify the UI
* filter.ts
* Refactors the TypeFilter to be a single class containing both namespace
and pod name values
* kubernetes-service.ts
* Add/remove the pod projects to/from the service depending on if there
are pods returned after filtering
* management-service.ts
* Add/remove the mgmt pod projects to/from the service depending on if
there are pods returned after filtering
* Discover context
* Use only 1 filter rather than an array
* Breaks out the empty state into its own component for use in other parts
of the Discover page
* DiscoverProjectContent.ts
* Needs refresh Effect to reset the pagination index if it is greater than
the project count returned
* DiscoverToolbar.ts
* When the buttons are clicked call the management service functions for
filtering and sorting so that all pod projects down to the k8 service
are effected.
* discover-service.ts
* Only responsible for grouping pods rather as filtering is re-assigned
to other services
---
.../src/client/namespace-client.ts | 165 ++++++++++++++--
packages/kubernetes-api/src/filter.ts | 75 ++++++++
packages/kubernetes-api/src/globals.ts | 2 +-
packages/kubernetes-api/src/index.ts | 2 +
.../kubernetes-api/src/kubernetes-service.ts | 97 ++++++++--
packages/kubernetes-api/src/sort.ts | 4 +
packages/management-api/src/globals.ts | 21 --
packages/management-api/src/managed-pod.ts | 22 +--
.../management-api/src/managed-project.ts | 10 +-
.../management-api/src/management-service.ts | 49 ++++-
.../online-shell/src/discover/Discover.tsx | 27 +--
.../src/discover/DiscoverEmptyContent.tsx | 44 +++++
.../src/discover/DiscoverProjectContent.tsx | 23 ++-
.../src/discover/DiscoverToolbar.tsx | 182 ++++++++++--------
packages/online-shell/src/discover/context.ts | 21 +-
.../src/discover/discover-project.ts | 26 +--
.../src/discover/discover-service.ts | 90 ++-------
17 files changed, 566 insertions(+), 294 deletions(-)
create mode 100644 packages/kubernetes-api/src/filter.ts
create mode 100644 packages/kubernetes-api/src/sort.ts
create mode 100644 packages/online-shell/src/discover/DiscoverEmptyContent.tsx
diff --git a/packages/kubernetes-api/src/client/namespace-client.ts b/packages/kubernetes-api/src/client/namespace-client.ts
index 7902c2dd..167c79ee 100644
--- a/packages/kubernetes-api/src/client/namespace-client.ts
+++ b/packages/kubernetes-api/src/client/namespace-client.ts
@@ -1,11 +1,13 @@
import jsonpath from 'jsonpath'
-import { JOLOKIA_PORT_QUERY, KubeObject, KubePod, Paging } from '../globals'
+import { JOLOKIA_PORT_QUERY, KubeObject, KubePod, Paging, log } from '../globals'
+import { TypeFilter } from '../filter'
import { WatchTypes } from '../model'
import { isObject } from '../utils'
+import { SortOrder } from '../sort'
import { Watched, KOptions, ProcessDataCallback } from './globals'
import { clientFactory } from './client-factory'
-export type NamespaceClientCallback = (jolokiaPods: KubePod[], jolokiaTotal: number, error?: Error) => void
+export type NamespaceClientCallback = (jolokiaPods: KubePod[], fullPodCount: number, error?: Error) => void
export interface Client {
watched: Watched
@@ -23,11 +25,17 @@ interface PodWatchers {
export class NamespaceClient implements Paging {
private _current = 0
+
private _podList: Set = new Set()
+ private _filteredList: string[] = []
+ private _notifyChange: boolean = false
+
private _podWatchers: PodWatchers = {}
private _nsWatcher?: Client
private _refreshing = 0
private _limit = 3
+ private _sortOrder?: SortOrder
+ private _typeFilter?: TypeFilter
constructor(
private _namespace: string,
@@ -44,6 +52,12 @@ export class NamespaceClient implements Paging {
this._callback(this.getJolokiaPods(), this._podList.size, cbError)
}
+ private isFiltered(): boolean {
+ if (!this._typeFilter) return false
+
+ return !this._typeFilter.filterNS(this._namespace)
+ }
+
private initPodOptions(kind: string, name?: string): KOptions {
const podOptions: KOptions = {
kind: kind,
@@ -58,24 +72,75 @@ export class NamespaceClient implements Paging {
}
private createPodWatchers() {
- if (this._podList.size === 0 || this._current >= this._podList.size) return
+ log.debug(`[NamespaceClient ${this._namespace}]: creating pod watchers`)
+
+ if (this._podList.size === 0 || this._current >= this._podList.size) {
+ log.debug(`[NamespaceClient ${this._namespace}]: no pods in namespace`)
+ this._callback(this.getJolokiaPods(), 0)
+ return
+ }
+
+ const sortFn = (a: string, b: string) => {
+ const order = this._sortOrder === SortOrder.DESC ? -1 : 1
+ return a.localeCompare(b) * order
+ }
+
+ /*
+ * Sort the pods according to the supplied sort order
+ * then selects the block of pods determined by current and limit
+ */
+ log.debug(`[NamespaceClient ${this._namespace}]: sort order: `, this._sortOrder, ' filter: ', this._typeFilter)
+
+ let pagedPods: string[]
+ if (this.isFiltered()) {
+ // Filtered out by namespace
+ this._filteredList = []
+ pagedPods = []
+ } else {
+ /*
+ * Filter out pods based on pod name and then sort
+ */
+ log.debug(`[NamespaceClient ${this._namespace}]: has pods`, this._podList)
+
+ this._filteredList = Array.from(this._podList)
+ .filter(podName => (!this._typeFilter) ? true : this._typeFilter.filterPod(podName))
+ .sort(sortFn)
- const podNames = Array.from(this._podList)
- .sort()
- .slice(this._current, this._current + this._limit)
+ log.debug(`[NamespaceClient ${this._namespace}]: pods after filtering`, this._podList)
- // Remove watchers for pods not in the slice of the sorted list
+ if (this._current >= this._filteredList.length) {
+ // if current is bigger than filtered podNames list then
+ // reset it back to 0
+ this._current = 0
+ }
+
+ // Set the paged pods list base on current and limit
+ pagedPods = this._filteredList
+ .slice(this._current, this._current + this._limit)
+ }
+
+ log.debug(`[NamespaceClient ${this._namespace}]: pods to be watched`, pagedPods)
+
+ /*
+ * Remove watchers for pods not in the slice of the filtered/sorted list
+ */
Object.entries(this._podWatchers)
- .filter(([name, _]) => {
- return !podNames.includes(name)
- })
+ .filter(([name, _]) => !pagedPods.includes(name))
.forEach(([name, podWatcher]) => {
+ log.debug(`[NamespaceClient ${this._namespace}]: deleting pod watcher [${name}]`)
+
clientFactory.destroy(podWatcher.client.watched, podWatcher.client.watch)
delete this._podWatchers[name]
+ this._notifyChange = true
})
- this._refreshing = podNames.length
- podNames.forEach(name => {
+ log.debug(`[NamespaceClient ${this._namespace}]: pod watchers already initialised`, this._podWatchers)
+
+ /*
+ * Create any new pod watchers
+ */
+ this._refreshing = pagedPods.length
+ pagedPods.forEach(name => {
// Already watching this pod
if (isObject(this._podWatchers[name])) {
this._refreshing--
@@ -87,17 +152,23 @@ export class NamespaceClient implements Paging {
const _podWatcher = _podClient.watch(podList => {
if (this._refreshing > 0) this._refreshing--
- if (podList.length === 0) return
-
- // podList should only contain 1 pod (due to name)
- this._podWatchers[name].jolokiaPod = podList[0]
+ if (podList.length > 0) {
+ if (this._podWatchers[name]) {
+ // podList should only contain 1 pod (due to name)
+ this._podWatchers[name].jolokiaPod = podList[0]
+ } else {
+ log.warn(`[NamespaceClient ${this._namespace}]: pod watcher with name ${name} no longer exists yet still watching`)
+ }
+ }
- if (this._refreshing === 0) {
+ if (this._refreshing <= 0) {
// Limit callback to final watch returning
- this._callback(this.getJolokiaPods(), this._podList.size)
+ this._callback(this.getJolokiaPods(), this._filteredList.length)
}
})
+ log.debug(`[NamespaceClient ${this._namespace}]: connecting new pod client`)
+
/*
* Pod is part of the current page so connect its pod watcher
*/
@@ -111,6 +182,16 @@ export class NamespaceClient implements Paging {
jolokiaPod: undefined,
}
})
+
+ if (pagedPods.length === 0 || this._notifyChange) {
+ log.debug(`[NamespaceClient ${this._namespace}]: notifying since page is either empty or watcher configuration has changed`)
+ this._notifyChange = false
+ this._callback(this.getJolokiaPods(), this._filteredList.length)
+ }
+ }
+
+ get namespace(): string {
+ return this._namespace
}
isConnected(): boolean {
@@ -123,11 +204,21 @@ export class NamespaceClient implements Paging {
this._limit = limit > 1 ? limit : 1
this._current = 0
+ if (this.isFiltered()) {
+ /*
+ * Do not watch since this namespace has been filtered out
+ * Return no pods and zero total
+ */
+ this._callback([], 0)
+ return
+ }
+
const _nsClient = clientFactory.create(this.initPodOptions(WatchTypes.PODS))
const _nsWatch = _nsClient.watch(pods => {
/*
- * Filter out any non-jolokia pods immediately and add
- * the applicable pods to the pod name list
+ * Filter out any non-jolokia pods or
+ * any pods that do not conform to any pod filters
+ * immediately and add the applicable pods to the pod name list
*/
const podNames: string[] = []
pods
@@ -187,6 +278,40 @@ export class NamespaceClient implements Paging {
delete pr.jolokiaPod
}
this._podWatchers = {}
+ this._podList.clear()
+ }
+
+ filter(typeFilter: TypeFilter) {
+ this._typeFilter = typeFilter
+ this._notifyChange = true
+
+ /*
+ * If already connected then recreate the pod watchers
+ * according to the new type filter
+ */
+ if (this.isConnected()) {
+ this.createPodWatchers()
+ }
+ else if (! this.isFiltered()) {
+ this.connect(this._limit)
+ }
+
+ /*
+ * Not connected and does not conform to the
+ * namespace part of the type filter
+ */
+ }
+
+ sort(sortOrder: SortOrder) {
+ this._sortOrder = sortOrder
+ this._notifyChange = true
+
+ /*
+ * If already connected then recreate the pod watchers
+ * according to the new sort order
+ */
+ if (this.isConnected())
+ this.createPodWatchers()
}
first() {
diff --git a/packages/kubernetes-api/src/filter.ts b/packages/kubernetes-api/src/filter.ts
new file mode 100644
index 00000000..1927143f
--- /dev/null
+++ b/packages/kubernetes-api/src/filter.ts
@@ -0,0 +1,75 @@
+import { KubePod } from "./globals"
+
+export enum TypeFilterType {
+ NAME = 'Name',
+ NAMESPACE = 'Namespace',
+}
+
+export function typeFilterTypeValueOf(str: string): TypeFilterType | undefined {
+ switch (str) {
+ case TypeFilterType.NAME:
+ return TypeFilterType.NAME
+ case TypeFilterType.NAMESPACE:
+ return TypeFilterType.NAMESPACE
+ default:
+ return undefined
+ }
+}
+
+export class TypeFilter {
+ private _nsValues: Set
+ private _nameValues: Set
+
+ constructor(nsValues?: string[], nameValues?: string[]) {
+ this._nsValues = new Set(nsValues ?? [])
+ this._nameValues = new Set(nameValues ?? [])
+ }
+
+ get nsValues(): string[] {
+ return Array.from(this._nsValues)
+ }
+
+ addNSValue(ns: string) {
+ this._nsValues.add(ns)
+ }
+
+ deleteNSValue(ns: string) {
+ this._nsValues.delete(ns)
+ }
+
+ get nameValues(): string[] {
+ return Array.from(this._nameValues)
+ }
+
+ addNameValue(name: string) {
+ this._nameValues.add(name)
+ }
+
+ deleteNameValue(name: string) {
+ this._nameValues.delete(name)
+ }
+
+ filterNS(ns: string): boolean {
+ if (this._nsValues.size === 0) return true
+
+ let resolved = false
+ this._nsValues.forEach(v => {
+ if (ns.includes(v))
+ resolved = true
+ })
+
+ return resolved
+ }
+
+ filterPod(podName: string): boolean {
+ if (this._nameValues.size === 0) return true
+
+ let resolved = false
+ this._nameValues.forEach(v => {
+ if (podName.includes(v))
+ resolved = true
+ })
+
+ return resolved
+ }
+}
diff --git a/packages/kubernetes-api/src/globals.ts b/packages/kubernetes-api/src/globals.ts
index fe333c81..9d26657e 100644
--- a/packages/kubernetes-api/src/globals.ts
+++ b/packages/kubernetes-api/src/globals.ts
@@ -43,7 +43,7 @@ export type KubeProject = KubeObject & {
}
export type KubePodsOrError = {
- total: number
+ fullPodCount: number
pods: KubePod[]
error?: Error
}
diff --git a/packages/kubernetes-api/src/index.ts b/packages/kubernetes-api/src/index.ts
index a6d90ee4..d580896d 100644
--- a/packages/kubernetes-api/src/index.ts
+++ b/packages/kubernetes-api/src/index.ts
@@ -15,5 +15,7 @@ export async function isK8ApiRegistered(): Promise {
}
export * from './globals'
+export * from './filter'
+export * from './sort'
export { k8Api, k8Service } from './init'
export * from './utils'
diff --git a/packages/kubernetes-api/src/kubernetes-service.ts b/packages/kubernetes-api/src/kubernetes-service.ts
index e00023b3..a5e0b7a9 100644
--- a/packages/kubernetes-api/src/kubernetes-service.ts
+++ b/packages/kubernetes-api/src/kubernetes-service.ts
@@ -13,6 +13,8 @@ import { isError, pathGet } from './utils'
import { clientFactory, log, Client, NamespaceClient } from './client'
import { K8Actions, KubePod, KubePodsByProject, KubeProject, Paging } from './globals'
import { k8Api } from './init'
+import { TypeFilter } from './filter'
+import { SortOrder } from './sort'
export class KubernetesService extends EventEmitter implements Paging {
private _loading = 0
@@ -25,6 +27,8 @@ export class KubernetesService extends EventEmitter implements Paging {
private namespace_clients: { [namespace: string]: NamespaceClient } = {}
private _nsLimit = 3
+ private _typeFilter?: TypeFilter
+ private _sortOrder?: SortOrder
async initialize(): Promise {
if (this._initialized) return this._initialized
@@ -55,11 +59,13 @@ export class KubernetesService extends EventEmitter implements Paging {
}
private initNamespaceClient(namespace: string) {
- const cb = (jolokiaPods: KubePod[], jolokiaTotal: number, error?: Error) => {
+ const cb = (jolokiaPods: KubePod[], fullPodCount: number, error?: Error) => {
+ log.debug(`[KubeService ${namespace}]: callback: fullPodCount: ${fullPodCount}`, 'jolokia pods: ', jolokiaPods)
+
this._loading = this._loading > 0 ? this._loading-- : 0
if (isError(error)) {
- this.podsByProject[namespace] = { total: jolokiaTotal, pods: [], error: error }
+ this.podsByProject[namespace] = { fullPodCount: fullPodCount, pods: [], error: error }
this.emit(K8Actions.CHANGED)
return
}
@@ -73,7 +79,12 @@ export class KubernetesService extends EventEmitter implements Paging {
projectPods.push(jpod)
}
- this.podsByProject[namespace] = { total: jolokiaTotal, pods: projectPods }
+ if (projectPods.length === 0)
+ delete this.podsByProject[namespace]
+ else {
+ this.podsByProject[namespace] = { fullPodCount: fullPodCount, pods: projectPods }
+ }
+
this.emit(K8Actions.CHANGED)
}
@@ -285,12 +296,40 @@ export class KubernetesService extends EventEmitter implements Paging {
return reason || 'unknown'
}
+ /*****************************
+ * Filtering and Sort support
+ *****************************/
+ sort(order: SortOrder) {
+ this._sortOrder = order
+
+ Object.values(this.namespace_clients)
+ .forEach(client => {
+ client.sort(order)
+ })
+ }
+
+ filter(typeFilter: TypeFilter) {
+ this._typeFilter = new TypeFilter(typeFilter.nsValues, typeFilter.nameValues)
+
+ Object.values(this.namespace_clients)
+ .forEach(client => {
+ client.filter(typeFilter)
+ })
+ }
+ /********************
+ * Paging interface
+ ********************/
first(namespace?: string) {
if (!namespace) return
- const namespaceClient = this.namespace_clients[namespace]
- if (!namespaceClient) return
+ const nsClient = this.namespace_clients[namespace]
+ if (!nsClient) return
+
+ if (! nsClient.isConnected) {
+ log.warn(`k8 Service cannot page on disconnected namespace ${namespace}`)
+ return
+ }
this.namespace_clients[namespace].first()
}
@@ -298,8 +337,10 @@ export class KubernetesService extends EventEmitter implements Paging {
hasPrevious(namespace?: string): boolean {
if (!namespace) return false
- const namespaceClient = this.namespace_clients[namespace]
- if (!namespaceClient) return false
+ const nsClient = this.namespace_clients[namespace]
+ if (!nsClient) return false
+
+ if (! nsClient.isConnected) return false
return this.namespace_clients[namespace].hasPrevious()
}
@@ -307,8 +348,13 @@ export class KubernetesService extends EventEmitter implements Paging {
previous(namespace?: string) {
if (!namespace) return
- const namespaceClient = this.namespace_clients[namespace]
- if (!namespaceClient) return
+ const nsClient = this.namespace_clients[namespace]
+ if (!nsClient) return
+
+ if (! nsClient.isConnected) {
+ log.warn(`k8 Service cannot page on disconnected namespace ${namespace}`)
+ return
+ }
this.namespace_clients[namespace].previous()
}
@@ -316,8 +362,10 @@ export class KubernetesService extends EventEmitter implements Paging {
hasNext(namespace?: string): boolean {
if (!namespace) return false
- const namespaceClient = this.namespace_clients[namespace]
- if (!namespaceClient) return false
+ const nsClient = this.namespace_clients[namespace]
+ if (!nsClient) return false
+
+ if (! nsClient.isConnected) return false
return this.namespace_clients[namespace].hasNext()
}
@@ -325,8 +373,13 @@ export class KubernetesService extends EventEmitter implements Paging {
next(namespace?: string) {
if (!namespace) return
- const namespaceClient = this.namespace_clients[namespace]
- if (!namespaceClient) return
+ const nsClient = this.namespace_clients[namespace]
+ if (!nsClient) return
+
+ if (! nsClient.isConnected) {
+ log.warn(`k8 Service cannot page on disconnected namespace ${namespace}`)
+ return
+ }
this.namespace_clients[namespace].next()
}
@@ -334,8 +387,13 @@ export class KubernetesService extends EventEmitter implements Paging {
last(namespace?: string) {
if (!namespace) return
- const namespaceClient = this.namespace_clients[namespace]
- if (!namespaceClient) return
+ const nsClient = this.namespace_clients[namespace]
+ if (!nsClient) return
+
+ if (! nsClient.isConnected) {
+ log.warn(`k8 Service cannot page on disconnected namespace ${namespace}`)
+ return
+ }
this.namespace_clients[namespace].last()
}
@@ -343,8 +401,13 @@ export class KubernetesService extends EventEmitter implements Paging {
page(pageIdx: number, namespace?: string) {
if (!namespace) return
- const namespaceClient = this.namespace_clients[namespace]
- if (!namespaceClient) return
+ const nsClient = this.namespace_clients[namespace]
+ if (!nsClient) return
+
+ if (! nsClient.isConnected) {
+ log.warn(`k8 Service cannot page on disconnected namespace ${namespace}`)
+ return
+ }
this.namespace_clients[namespace].page(pageIdx)
}
diff --git a/packages/kubernetes-api/src/sort.ts b/packages/kubernetes-api/src/sort.ts
new file mode 100644
index 00000000..21ac7058
--- /dev/null
+++ b/packages/kubernetes-api/src/sort.ts
@@ -0,0 +1,4 @@
+export enum SortOrder {
+ ASC = 'Ascending',
+ DESC = 'Descending'
+}
diff --git a/packages/management-api/src/globals.ts b/packages/management-api/src/globals.ts
index f2ce3c47..91fac355 100644
--- a/packages/management-api/src/globals.ts
+++ b/packages/management-api/src/globals.ts
@@ -15,24 +15,3 @@ export enum MgmtActions {
export type MPodsByUid = { [uid: string]: ManagedPod }
export type ManagedProjects = { [key: string]: ManagedProject }
-
-export enum TypeFilterType {
- NAME = 'Name',
- NAMESPACE = 'Namespace',
-}
-
-export function typeFilterTypeValueOf(str: string): TypeFilterType | undefined {
- switch (str) {
- case TypeFilterType.NAME:
- return TypeFilterType.NAME
- case TypeFilterType.NAMESPACE:
- return TypeFilterType.NAMESPACE
- default:
- return undefined
- }
-}
-
-export type TypeFilter = {
- type: TypeFilterType
- values: Set
-}
diff --git a/packages/management-api/src/managed-pod.ts b/packages/management-api/src/managed-pod.ts
index 00d58383..6f3a1306 100644
--- a/packages/management-api/src/managed-pod.ts
+++ b/packages/management-api/src/managed-pod.ts
@@ -5,7 +5,7 @@ import Jolokia, {
} from 'jolokia.js'
import 'jolokia.js/simple'
import $ from 'jquery'
-import { TypeFilter, log } from './globals'
+import { log } from './globals'
import jsonpath from 'jsonpath'
import {
k8Api,
@@ -14,6 +14,7 @@ import {
PodStatus,
joinPaths,
PodSpec,
+ TypeFilter,
JOLOKIA_PORT_QUERY,
} from '@hawtio/online-kubernetes-api'
import { ParseResult, isJolokiaVersionResponseType, jolokiaResponseParse } from './jolokia-response-utils'
@@ -259,23 +260,4 @@ export class ManagedPod {
eventService.notify({ type: 'danger', message: msg })
}
}
-
- filter(filter: TypeFilter): boolean {
- const metadata = this.metadata
- if (!metadata) return false
-
- type KubeObjKey = keyof typeof metadata
- const podProp = metadata[filter.type.toLowerCase() as KubeObjKey] as string
-
- // Want to filter on this property but value
- // is null so filter fails
- if (!podProp) return false
-
- // values is tested as OR
- for (const value of filter.values) {
- if (podProp.toLowerCase().includes(value.toLowerCase())) return true
- }
-
- return false
- }
}
diff --git a/packages/management-api/src/managed-project.ts b/packages/management-api/src/managed-project.ts
index bf897200..eba20458 100644
--- a/packages/management-api/src/managed-project.ts
+++ b/packages/management-api/src/managed-project.ts
@@ -5,7 +5,7 @@ import { ManagedPod } from './managed-pod'
export class ManagedProject {
private _error?: Error
private podsByUid: MPodsByUid = {}
- private _podTotal: number = 0
+ private _fullPodCount: number = 0
constructor(private _name: string) {}
@@ -13,12 +13,12 @@ export class ManagedProject {
return this._name
}
- get podTotal(): number {
- return this._podTotal
+ get fullPodCount(): number {
+ return this._fullPodCount
}
- set podTotal(podTotal: number) {
- this._podTotal = podTotal
+ set fullPodCount(fullPodCount: number) {
+ this._fullPodCount = fullPodCount
}
get error(): Error | undefined {
diff --git a/packages/management-api/src/management-service.ts b/packages/management-api/src/management-service.ts
index 4a05da8f..5d4d1cea 100644
--- a/packages/management-api/src/management-service.ts
+++ b/packages/management-api/src/management-service.ts
@@ -10,6 +10,8 @@ import {
debounce,
KubePodsByProject,
Paging,
+ SortOrder,
+ TypeFilter
} from '@hawtio/online-kubernetes-api'
import { ManagedProjects, MgmtActions, log } from './globals'
import { ManagedProject } from './managed-project'
@@ -32,6 +34,7 @@ export class ManagementService extends EventEmitter implements Paging {
fireUpdate: false,
uids: new Set(),
}
+ private _orderingAndFiltering: boolean = false
private _jolokiaPolling = 15000
private _pollingHandle?: NodeJS.Timeout
@@ -55,6 +58,20 @@ export class ManagementService extends EventEmitter implements Paging {
if (!this.hasError()) {
const kPodsByProject: KubePodsByProject = k8Service.getPods()
+ /*
+ * Delete any projects no longer contained in the k8 service
+ */
+ Object.keys(this._managedProjects)
+ .filter(ns => ! Object.keys(kPodsByProject).includes(ns))
+ .forEach(ns => {
+ /* Flag this project to be removed in the update */
+ this._managedProjects[ns].pods = []
+ this._managedProjects[ns].fullPodCount = 0
+ })
+
+ /*
+ * Update the remaining projects
+ */
Object.entries(kPodsByProject).forEach(([project, kPodsOrError]) => {
/*
* Either project has never been seen before so initialise
@@ -68,10 +85,11 @@ export class ManagementService extends EventEmitter implements Paging {
// Project may have an error
mgmtProject.error = kPodsOrError
- mgmtProject.podTotal = kPodsOrError.total
mgmtProject.pods = kPodsOrError.pods
+ mgmtProject.fullPodCount = kPodsOrError.fullPodCount
})
+
// let's kick a polling cycle
this.pollManagementData()
}
@@ -90,7 +108,7 @@ export class ManagementService extends EventEmitter implements Paging {
private preMgmtUpdate() {
/* Reset the update queue */
this.updateQueue.uids.clear()
- this.updateQueue.fireUpdate = false
+ this.updateQueue.fireUpdate = this._orderingAndFiltering
// Add all the uids to the queue
Object.values(this._managedProjects)
@@ -103,10 +121,17 @@ export class ManagementService extends EventEmitter implements Paging {
this.updateQueue.uids.delete(emitter.uid)
}
- /* If the emitter should fire then update the queue */
- this.updateQueue.fireUpdate = emitter.fireUpdate ? emitter.fireUpdate : this.updateQueue.fireUpdate
+ /*
+ * If not already set to fire then check whether
+ * the emitter should fire then update the queue
+ */
+ if (!this.updateQueue.fireUpdate)
+ this.updateQueue.fireUpdate = emitter.fireUpdate ? emitter.fireUpdate : this.updateQueue.fireUpdate
- if (this.updateQueue.fireUpdate && this.updateQueue.uids.size === 0) this.emit(MgmtActions.UPDATED)
+ if (this.updateQueue.fireUpdate && this.updateQueue.uids.size === 0) {
+ this._orderingAndFiltering = false // reset ready for next time
+ this.emit(MgmtActions.UPDATED)
+ }
}
private async mgmtUpdate() {
@@ -126,6 +151,7 @@ export class ManagementService extends EventEmitter implements Paging {
const mPodsByUid = managedProject.pods
if (Object.entries(mPodsByUid).length === 0) {
+ delete this._managedProjects[managedProject.name]
this.emitUpdate({ fireUpdate: true })
continue
}
@@ -367,6 +393,19 @@ export class ManagementService extends EventEmitter implements Paging {
connectService.connect(connection)
}
+ /********************
+ * Filtering & Sort support
+ ********************/
+ filter(typeFilter: TypeFilter) {
+ this._orderingAndFiltering = true
+ k8Service.filter(typeFilter)
+ }
+
+ sort(sortOrder: SortOrder) {
+ this._orderingAndFiltering = true
+ k8Service.sort(sortOrder)
+ }
+
/********************
* Paging interface
********************/
diff --git a/packages/online-shell/src/discover/Discover.tsx b/packages/online-shell/src/discover/Discover.tsx
index be143ae9..49917915 100644
--- a/packages/online-shell/src/discover/Discover.tsx
+++ b/packages/online-shell/src/discover/Discover.tsx
@@ -3,26 +3,22 @@ import {
Alert,
Card,
CardBody,
- EmptyState,
- EmptyStateBody,
- EmptyStateIcon,
PageSection,
PageSectionVariants,
Title,
- EmptyStateHeader,
Tabs,
TabTitleText,
Tab,
} from '@patternfly/react-core'
-import { CubesIcon } from '@patternfly/react-icons'
import { discoverService } from './discover-service'
import { DiscoverToolbar } from './DiscoverToolbar'
import { DiscoverContext, useDisplayItems } from './context'
import { DiscoverProjectContent } from './DiscoverProjectContent'
import { DiscoverLoadingPanel } from './DiscoverLoadingPanel'
+import { DiscoverEmptyContent } from './DiscoverEmptyContent'
export const Discover: React.FunctionComponent = () => {
- const { error, isLoading, refreshing, setRefreshing, discoverProjects, setDiscoverProjects, filters, setFilters } =
+ const { error, isLoading, refreshing, setRefreshing, discoverProjects, setDiscoverProjects, filter, setFilter } =
useDisplayItems()
const [activeTabKey, setActiveTabKey] = React.useState('')
@@ -44,7 +40,7 @@ export const Discover: React.FunctionComponent = () => {
return activeKey
})
- }, [isLoading, error, discoverProjects, filters])
+ }, [isLoading, error, discoverProjects, filter])
if (isLoading) {
return (
@@ -78,23 +74,14 @@ export const Discover: React.FunctionComponent = () => {
setRefreshing,
discoverProjects,
setDiscoverProjects,
- filters,
- setFilters,
+ filter,
+ setFilter,
}}
>
{discoverProjects.length === 0 && (
-
- }
- headingLevel='h1'
- />
-
- There are no containers running with a port configured whose name is jolokia
.
-
-
+
)}
{discoverProjects.length > 0 && (
@@ -102,7 +89,7 @@ export const Discover: React.FunctionComponent = () => {
{discoverProjects.map(discoverProject => (
{`${discoverProject.name} (${discoverProject.podsTotal})`}}
+ title={{`${discoverProject.name} (${discoverProject.fullPodCount})`}}
key={`discover-project-${discoverProject.name}`}
>
diff --git a/packages/online-shell/src/discover/DiscoverEmptyContent.tsx b/packages/online-shell/src/discover/DiscoverEmptyContent.tsx
new file mode 100644
index 00000000..0752ac06
--- /dev/null
+++ b/packages/online-shell/src/discover/DiscoverEmptyContent.tsx
@@ -0,0 +1,44 @@
+import React, { useContext } from 'react'
+import {
+ EmptyState,
+ EmptyStateHeader,
+ EmptyStateIcon,
+ EmptyStateBody,
+ Panel,
+ PanelMain,
+ PanelMainBody,
+} from '@patternfly/react-core'
+import { CubesIcon } from '@patternfly/react-icons'
+import { DiscoverContext } from './context'
+import { DiscoverLoadingPanel } from './DiscoverLoadingPanel'
+
+export const DiscoverEmptyContent: React.FunctionComponent = () => {
+ const { refreshing } = useContext(DiscoverContext)
+
+ return (
+
+ {refreshing && (
+
+
+
+
+
+
+
+ )}
+
+ {! refreshing && (
+
+ }
+ headingLevel='h1'
+ />
+
+ There are no containers running with a port configured whose name is jolokia
.
+
+
+ )}
+
+ )
+}
diff --git a/packages/online-shell/src/discover/DiscoverProjectContent.tsx b/packages/online-shell/src/discover/DiscoverProjectContent.tsx
index 9c56ec7f..2c597b3a 100644
--- a/packages/online-shell/src/discover/DiscoverProjectContent.tsx
+++ b/packages/online-shell/src/discover/DiscoverProjectContent.tsx
@@ -1,6 +1,5 @@
-import React, { useContext } from 'react'
+import React, { useContext, useEffect } from 'react'
import {
- Button,
List,
Pagination,
Panel,
@@ -12,13 +11,13 @@ import {
ToolbarGroup,
ToolbarItem,
} from '@patternfly/react-core'
-import { DiscoverProject } from './discover-project'
+import { k8Service } from '@hawtio/online-kubernetes-api'
+import { mgmtService } from '@hawtio/online-management-api'
import { DiscoverGroupList } from './DiscoverGroupList'
import { DiscoverPodItem } from './DiscoverPodItem'
-import { mgmtService } from '@hawtio/online-management-api'
import { DiscoverLoadingPanel } from './DiscoverLoadingPanel'
import { DiscoverContext } from './context'
-import { k8Service } from '@hawtio/online-kubernetes-api'
+import { DiscoverProject } from './discover-project'
interface DiscoverProjectCntProps {
project: DiscoverProject
@@ -30,6 +29,18 @@ export const DiscoverProjectContent: React.FunctionComponent {
+ if (refreshing) return
+
+ if ((page * k8Service.namespaceLimit) > (props.project.fullPodCount + k8Service.namespaceLimit)) {
+ /*
+ * If filtering has caused the fullPodCount to decrease
+ * below the (page * limit) total then reset back to 1
+ */
+ setPage(1)
+ }
+ }, [refreshing])
+
const firstPods = (page: number) => {
setPage(page)
setRefreshing(true)
@@ -70,7 +81,7 @@ export const DiscoverProjectContent: React.FunctionComponent }
-const descending: SortOrder = { id: 'descending', icon: }
-
-interface SortMetaType {
- id: string
- order: SortOrder
+function sortTypeValueOf(str: string): SortType | undefined {
+ switch (str) {
+ case SortType.NAME:
+ return SortType.NAME
+ case SortType.NAMESPACE:
+ return SortType.NAMESPACE
+ default:
+ return undefined
+ }
}
-type FilterMeta = {
- [name: string]: TypeFilterType
+interface SortOrderIcon {
+ type: SortOrder
+ icon: ReactNode
}
-type SortMeta = {
- [name: string]: SortMetaType
-}
+const ascending: SortOrderIcon = { type: SortOrder.ASC, icon: }
+const descending: SortOrderIcon = { type: SortOrder.DESC, icon: }
-const filterMeta: FilterMeta = {
+const filterMeta = {
name: TypeFilterType.NAME,
namespace: TypeFilterType.NAMESPACE,
}
-const sortMeta: SortMeta = {
+const sortMeta = {
name: {
- id: 'Name',
- order: ascending,
+ id: SortType.NAME,
+ orderIcon: ascending,
},
namespace: {
- id: 'Namespace',
- order: ascending,
+ id: SortType.NAMESPACE,
+ orderIcon: ascending,
},
}
export const DiscoverToolbar: React.FunctionComponent = () => {
- const { discoverProjects, setDiscoverProjects, filters, setFilters } = useContext(DiscoverContext)
+ const {
+ discoverProjects, setDiscoverProjects,
+ filter, setFilter, setRefreshing } = useContext(DiscoverContext)
// Ref for toggle of filter type Select control
const filterTypeToggleRef = useRef()
@@ -73,18 +79,23 @@ export const DiscoverToolbar: React.FunctionComponent = () => {
const [filterInputPlaceholder, setFilterInputPlaceholder] = useState(defaultFilterInputPlaceholder)
// Ref for toggle of sort type Select control
- useRef()
+ const sortTypeToggleRef = useRef()
// The type of sort to be created - chosen by the Select control
- const [sortType, setSortType] = useState(sortMeta.name.id || '')
+ const [sortType, setSortType] = useState(sortMeta.name.id || '')
// Flag to determine whether the Sort Select control is open or closed
const [isSortTypeOpen, setIsSortTypeOpen] = useState(false)
// Icon showing the sort
- const [sortOrder, setSortOrder] = useState(sortMeta.name.order)
+ const [sortOrderIcon, setSortOrderIcon] = useState(sortMeta.name.orderIcon)
+
+ const callMgmtFilter = (filter: TypeFilter) => {
+ setRefreshing(true)
+ setFilter(filter)
+ mgmtService.filter(filter)
+ }
const clearFilters = () => {
- const emptyFilters: TypeFilter[] = []
- setFilters(emptyFilters)
setFilterInput('')
+ callMgmtFilter(new TypeFilter())
}
const onSelectFilterType = (_event?: ChangeEvent | MouseEvent, value?: string | number) => {
@@ -100,9 +111,11 @@ export const DiscoverToolbar: React.FunctionComponent = () => {
const filterChips = (): string[] => {
const chips: string[] = []
- filters.forEach(filter => {
- Array.from(filter.values).map(v => chips.push(filter.type + ':' + v))
- })
+ filter.nsValues
+ .map(v => chips.push(TypeFilterType.NAMESPACE + ':' + v))
+
+ filter.nameValues
+ .map(v => chips.push(TypeFilterType.NAME + ':' + v))
return chips
}
@@ -112,71 +125,91 @@ export const DiscoverToolbar: React.FunctionComponent = () => {
if (!filterType) return
- const newFilters = [...filters]
-
- const typeFilter = newFilters.filter(f => f.type === filterType)
- if (typeFilter.length === 0) {
- const filter: TypeFilter = {
- type: filterType,
- values: new Set([value]),
- }
- newFilters.push(filter)
- } else {
- typeFilter[0].values.add(value)
+ const newTypeFilter = new TypeFilter(filter.nsValues, filter.nameValues)
+
+ switch (filterType) {
+ case TypeFilterType.NAME:
+ newTypeFilter.addNameValue(value)
+ break
+ case TypeFilterType.NAMESPACE:
+ newTypeFilter.addNSValue(value)
}
- setFilters(newFilters)
+ callMgmtFilter(newTypeFilter)
}
const deleteFilter = (filterChip: string) => {
- const remaining = filters.filter(filter => {
- const [typeId, value] = filterChip.split(':')
- const type = typeFilterTypeValueOf(typeId)
-
- if (filter.type !== type) return true
+ const [typeId, value] = filterChip.split(':')
+ const filterType = typeFilterTypeValueOf(typeId)
+ const newTypeFilter = new TypeFilter(filter.nsValues, filter.nameValues)
- filter.values.delete(value)
- return filter.values.size > 0
- })
+ switch (filterType) {
+ case TypeFilterType.NAME:
+ newTypeFilter.deleteNameValue(value)
+ break
+ case TypeFilterType.NAMESPACE:
+ newTypeFilter.deleteNSValue(value)
+ }
- setFilters(remaining)
+ callMgmtFilter(newTypeFilter)
}
const onSelectSortType = (_event?: MouseEvent, value?: string | number) => {
if (!value) return
// Updates the sort type either Name or Namespace
- setSortType(value as string)
+ const type = sortTypeValueOf(`${value}`)
+ setSortType(type as SortType)
// Updates the sort order to whichever the sort type is set to
- setSortOrder(value === sortMeta.name.id ? sortMeta.name.order : sortMeta.namespace.order)
+ setSortOrderIcon(value === sortMeta.name.id ? sortMeta.name.orderIcon : sortMeta.namespace.orderIcon)
setIsSortTypeOpen(false)
- filterTypeToggleRef?.current?.focus()
+ sortTypeToggleRef?.current?.focus()
+ }
+
+ const isSortButtonEnabled = () => {
+ console.log(`SortType ${sortType}`)
+ switch (sortType) {
+ case SortType.NAMESPACE:
+ return Object.values(discoverProjects).length > 1
+ case SortType.NAME:
+ return discoverProjects
+ .filter(discoverProject => (discoverProject.fullPodCount) > 1)
+ .length > 0
+ default:
+ return true // enable by default
+ }
}
const sortItems = () => {
- const sortedProjects = [...discoverProjects]
- const newSortOrder = sortOrder === ascending ? descending : ascending
+ const newSortOrderIcon = sortOrderIcon === ascending ? descending : ascending
+ setSortOrderIcon(newSortOrderIcon)
+
+ switch(sortType) {
+ case SortType.NAMESPACE:
+ const sortedProjects = [...discoverProjects]
+ /*
+ * Sorting via namespace requires simply moving around the
+ * tabs in the UI
+ */
+ sortMeta.namespace.orderIcon = newSortOrderIcon.type === SortOrder.ASC ? ascending : descending
- switch (sortType) {
- case sortMeta.name.id:
- // Sorting via name
- sortMeta.name.order = newSortOrder
- sortedProjects.forEach(project => {
- project.sort(newSortOrder === ascending ? 1 : -1)
- })
- break
- case sortMeta.namespace.id:
- // Sorting via namespace
- sortMeta.namespace.order = newSortOrder
sortedProjects.sort((ns1: DiscoverProject, ns2: DiscoverProject) => {
let value = ns1.name.localeCompare(ns2.name)
- return newSortOrder === descending ? (value *= -1) : value
+ return newSortOrderIcon === descending ? (value *= -1) : value
})
- }
- setDiscoverProjects(sortedProjects)
- setSortOrder(newSortOrder)
+ setDiscoverProjects(sortedProjects)
+ break
+ case SortType.NAME:
+ /*
+ * Resorting the pod names required going back to k8 level
+ * in order to correctly sort all pods and get back the right
+ * set according to the paging limit
+ */
+ setRefreshing(true)
+ mgmtService.sort(newSortOrderIcon.type)
+ }
}
return (
@@ -225,15 +258,14 @@ export const DiscoverToolbar: React.FunctionComponent = () => {
/>
+
diff --git a/packages/online-shell/src/discover/context.ts b/packages/online-shell/src/discover/context.ts
index 602abce3..907d2b99 100644
--- a/packages/online-shell/src/discover/context.ts
+++ b/packages/online-shell/src/discover/context.ts
@@ -1,5 +1,6 @@
import { createContext, useCallback, useEffect, useRef, useState } from 'react'
-import { MgmtActions, isMgmtApiRegistered, mgmtService, TypeFilter } from '@hawtio/online-management-api'
+import { TypeFilter } from '@hawtio/online-kubernetes-api'
+import { MgmtActions, isMgmtApiRegistered, mgmtService } from '@hawtio/online-management-api'
import { discoverService } from './discover-service'
import { DiscoverProject } from './discover-project'
@@ -17,16 +18,16 @@ export function useDisplayItems() {
const [error, setError] = useState()
const [discoverProjects, setDiscoverProjects] = useState([])
- // Set of filters created by filter control and displayed as chips
- const [filters, setFilters] = useState([])
+ // type filter created by filter control and displayed as chips
+ const [filter, setFilter] = useState(new TypeFilter())
const organisePods = useCallback(() => {
- const discoverProjects = discoverService.filterAndGroupPods(filters)
+ const discoverProjects = discoverService.groupPods()
setDiscoverProjects([...discoverProjects])
setIsLoading(false)
setRefreshing(false)
- }, [filters])
+ }, [])
useEffect(() => {
const waitLoading = async () => {
@@ -63,7 +64,7 @@ export function useDisplayItems() {
}
}, [organisePods])
- return { error, isLoading, refreshing, setRefreshing, discoverProjects, setDiscoverProjects, filters, setFilters }
+ return { error, isLoading, refreshing, setRefreshing, discoverProjects, setDiscoverProjects, filter, setFilter }
}
type DiscoverContext = {
@@ -71,8 +72,8 @@ type DiscoverContext = {
setRefreshing: (refresh: boolean) => void
discoverProjects: DiscoverProject[]
setDiscoverProjects: (projects: DiscoverProject[]) => void
- filters: TypeFilter[]
- setFilters: (filters: TypeFilter[]) => void
+ filter: TypeFilter
+ setFilter: (filter: TypeFilter) => void
}
export const DiscoverContext = createContext({
@@ -84,8 +85,8 @@ export const DiscoverContext = createContext({
setDiscoverProjects: () => {
// no-op
},
- filters: [],
- setFilters: () => {
+ filter: new TypeFilter(),
+ setFilter: () => {
// no-op
},
})
diff --git a/packages/online-shell/src/discover/discover-project.ts b/packages/online-shell/src/discover/discover-project.ts
index 96107c2b..40aa1a3e 100644
--- a/packages/online-shell/src/discover/discover-project.ts
+++ b/packages/online-shell/src/discover/discover-project.ts
@@ -1,23 +1,22 @@
import { ManagedPod } from '@hawtio/online-management-api'
-import { DiscoverGroup, DiscoverPod, DiscoverType } from './globals'
import { OwnerReference } from '@hawtio/online-kubernetes-api'
+import { DiscoverGroup, DiscoverPod, DiscoverType } from './globals'
export type DiscoverProjects = {
[name: string]: DiscoverProject
}
-type SortOrderType = 1 | -1
-
export class DiscoverProject {
private discoverGroups: DiscoverGroup[] = []
private discoverPods: DiscoverPod[] = []
+ private _fullPodCount: number = 0
constructor(
private projectName: string,
- private totalPods: number,
+ fullPodCount: number,
mgmtPods: ManagedPod[],
) {
- this.refresh(mgmtPods)
+ this.refresh(fullPodCount, mgmtPods)
}
private podOwner(pod: ManagedPod): OwnerReference | null {
@@ -68,7 +67,7 @@ export class DiscoverProject {
return values[key]
}
- refresh(pods: ManagedPod[]) {
+ refresh(fullPodCount: number, pods: ManagedPod[]) {
const discoverPods: DiscoverPod[] = []
const discoverGroups: DiscoverGroup[] = []
@@ -97,6 +96,7 @@ export class DiscoverProject {
this.discoverGroups = discoverGroups
this.discoverPods = discoverPods
+ this._fullPodCount = fullPodCount
/**
* Notify event service of any errors in the groups and pods
@@ -112,8 +112,8 @@ export class DiscoverProject {
return this.projectName
}
- get podsTotal(): number {
- return this.totalPods
+ get fullPodCount(): number {
+ return this._fullPodCount
}
get pods(): DiscoverPod[] {
@@ -123,14 +123,4 @@ export class DiscoverProject {
get groups(): DiscoverGroup[] {
return this.discoverGroups
}
-
- sort(sortOrder: SortOrderType) {
- this.discoverGroups.sort((a: DiscoverGroup, b: DiscoverGroup) => {
- return a.name.localeCompare(b.name) * sortOrder
- })
-
- this.discoverPods.sort((a: DiscoverPod, b: DiscoverPod) => {
- return a.name.localeCompare(b.name) * sortOrder
- })
- }
}
diff --git a/packages/online-shell/src/discover/discover-service.ts b/packages/online-shell/src/discover/discover-service.ts
index 96103416..898a08f6 100644
--- a/packages/online-shell/src/discover/discover-service.ts
+++ b/packages/online-shell/src/discover/discover-service.ts
@@ -1,7 +1,7 @@
-import { mgmtService, MPodsByUid, TypeFilterType, TypeFilter } from '@hawtio/online-management-api'
+import { TypeFilterType, TypeFilter } from '@hawtio/online-kubernetes-api'
+import { ManagedProject, mgmtService, ManagedPod } from '@hawtio/online-management-api'
import { DiscoverPod } from './globals'
import { DiscoverProject, DiscoverProjects } from './discover-project'
-import { ManagedProject } from '@hawtio/online-management-api'
export enum ViewType {
listView = 'listView',
@@ -11,88 +11,26 @@ export enum ViewType {
class DiscoverService {
private discoverProjects: DiscoverProjects = {}
- filterAndGroupPods(filters: TypeFilter[]): DiscoverProject[] {
- /*
- * Find all the namespace filters and reduce them together
- */
- const nsFilters = filters.filter(f => f.type === TypeFilterType.NAMESPACE)
- let nsFilter: TypeFilter | undefined
- if (nsFilters.length === 0) nsFilter = undefined
- else {
- nsFilter = nsFilters.reduceRight((accumulator, currentValue, currentIndex, array) => {
- currentValue.values.forEach(v => accumulator.values.add(v))
- return accumulator
- })
- }
-
- const podFilters = filters.filter(f => f.type === TypeFilterType.NAME)
-
- const filteredProjects: ManagedProject[] = []
-
+ groupPods(): DiscoverProject[] {
+ const projectNames: string[] = []
Object.values(mgmtService.projects).forEach(mgmtProject => {
- if (!nsFilter) {
- filteredProjects.push(mgmtProject)
- return
- }
-
- /*
- * Namespace filter values will be tested as an OR filter
- * This corresponds to Patternfly design guidelines that
- * "[...] there is an "AND" relationship between facets,
- * and an "OR" relationship between values."
- * (https://www.patternfly.org/patterns/filters/design-guidelines/#filter-group)
- */
- let exclude = true
- nsFilter.values.forEach(v => {
- if (mgmtProject.name.includes(v)) {
- filteredProjects.push(mgmtProject)
- exclude = false
- }
- })
-
- if (exclude) {
- /*
- * By removing any projects that do not correspond to the namespace
- * filter we are effectively doing an AND test with the name filter below
- *
- * ie. if the project fails the namespace filter then it should not
- * even be tested against the name filter
- */
- delete this.discoverProjects[mgmtProject.name]
- }
- })
-
- filteredProjects.forEach(mgmtProject => {
- const podsByUid: MPodsByUid = mgmtProject.pods
-
- const filtered = Object.values(podsByUid).filter(pod => {
- if (podFilters.length === 0) return true
-
- for (const f of podFilters) {
- if (pod.filter(f)) {
- return true // Include as it conforms to at least one filter
- }
- }
-
- return false
- })
-
- if (filtered.length === 0) {
- // Remove the project as no longer contains any pods
- if (this.discoverProjects[mgmtProject.name]) {
- delete this.discoverProjects[mgmtProject.name]
- }
- return
- }
+ const pods: ManagedPod[] = Object.values(mgmtProject.pods)
+ projectNames.push(mgmtProject.name)
if (!this.discoverProjects[mgmtProject.name]) {
- const discoverProject = new DiscoverProject(mgmtProject.name, mgmtProject.podTotal, filtered)
+ const discoverProject = new DiscoverProject(mgmtProject.name, mgmtProject.fullPodCount, pods)
this.discoverProjects[mgmtProject.name] = discoverProject
} else {
- this.discoverProjects[mgmtProject.name].refresh(filtered)
+ this.discoverProjects[mgmtProject.name].refresh(mgmtProject.fullPodCount, pods)
}
})
+ Object.keys(this.discoverProjects)
+ .filter(ns => ! projectNames.includes(ns))
+ .forEach(ns => {
+ delete this.discoverProjects[ns]
+ })
+
return Object.values(this.discoverProjects)
}