From 6f6bf3f3b3fa6f38997d9f9f8ef7c4beecb09fee Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 24 Jun 2020 11:14:45 -0700 Subject: [PATCH] Fix Arc Postgres refresh (#11074) --- extensions/arc/src/models/postgresModel.ts | 4 +- .../postgres/postgresConnectionStringsPage.ts | 27 +++- .../dashboards/postgres/postgresDashboard.ts | 4 +- .../postgres/postgresOverviewPage.ts | 132 ++++++++++++------ .../postgres/postgresPropertiesPage.ts | 37 +++-- .../postgres/postgresResourceHealthPage.ts | 70 +++++++--- 6 files changed, 192 insertions(+), 82 deletions(-) diff --git a/extensions/arc/src/models/postgresModel.ts b/extensions/arc/src/models/postgresModel.ts index e23eaaf2ce..9a1ac6bd7c 100644 --- a/extensions/arc/src/models/postgresModel.ts +++ b/extensions/arc/src/models/postgresModel.ts @@ -172,10 +172,10 @@ export class PostgresModel extends ResourceModel { } /** Given a V1Pod returns its status */ - public static getPodStatus(pod: V1Pod) { + public static getPodStatus(pod: V1Pod): string { const phase = pod.status?.phase; if (phase !== 'Running') { - return phase; + return phase ?? ''; } // Pods can be in the running phase while some diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresConnectionStringsPage.ts b/extensions/arc/src/ui/dashboards/postgres/postgresConnectionStringsPage.ts index f16e0754e8..bbaf84fc4b 100644 --- a/extensions/arc/src/ui/dashboards/postgres/postgresConnectionStringsPage.ts +++ b/extensions/arc/src/ui/dashboards/postgres/postgresConnectionStringsPage.ts @@ -7,18 +7,19 @@ import * as vscode from 'vscode'; import * as azdata from 'azdata'; import * as loc from '../../../localizedConstants'; import { IconPathHelper, cssStyles } from '../../../constants'; -import { KeyValueContainer, InputKeyValue } from '../../components/keyValueContainer'; +import { KeyValueContainer, InputKeyValue, KeyValue } from '../../components/keyValueContainer'; import { DashboardPage } from '../../components/dashboardPage'; import { PostgresModel } from '../../../models/postgresModel'; export class PostgresConnectionStringsPage extends DashboardPage { + private loading?: azdata.LoadingComponent; private keyValueContainer?: KeyValueContainer; constructor(protected modelView: azdata.ModelView, private _postgresModel: PostgresModel) { super(modelView); this.disposables.push(this._postgresModel.onServiceUpdated( - () => this.eventuallyRunOnInitialized(() => this.refresh()))); + () => this.eventuallyRunOnInitialized(() => this.handleServiceUpdated()))); } protected get title(): string { @@ -58,8 +59,14 @@ export class PostgresConnectionStringsPage extends DashboardPage { infoAndLink.addItem(link); content.addItem(infoAndLink, { CSSStyles: { 'margin-bottom': '25px' } }); - this.keyValueContainer = new KeyValueContainer(this.modelView.modelBuilder, []); - content.addItem(this.keyValueContainer.container); + this.keyValueContainer = new KeyValueContainer(this.modelView.modelBuilder, this.getConnectionStrings()); + this.loading = this.modelView.modelBuilder.loadingComponent() + .withItem(this.keyValueContainer.container) + .withProperties({ + loading: !this._postgresModel.serviceLastUpdated + }).component(); + + content.addItem(this.loading); this.initialized = true; return root; } @@ -74,6 +81,7 @@ export class PostgresConnectionStringsPage extends DashboardPage { refreshButton.onDidClick(async () => { refreshButton.enabled = false; try { + this.loading!.loading = true; await this._postgresModel.refresh(); } catch (error) { vscode.window.showErrorMessage(loc.refreshFailed(error)); @@ -87,10 +95,10 @@ export class PostgresConnectionStringsPage extends DashboardPage { ]).component(); } - private refresh() { + private getConnectionStrings(): KeyValue[] { const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint; - this.keyValueContainer?.refresh([ + return [ new InputKeyValue('ADO.NET', `Server=${endpoint.ip};Database=postgres;Port=${endpoint.port};User Id=postgres;Password={your_password_here};Ssl Mode=Require;`), new InputKeyValue('C++ (libpq)', `host=${endpoint.ip} port=${endpoint.port} dbname=postgres user=postgres password={your_password_here} sslmode=require`), new InputKeyValue('JDBC', `jdbc:postgresql://${endpoint.ip}:${endpoint.port}/postgres?user=postgres&password={your_password_here}&sslmode=require`), @@ -100,6 +108,11 @@ export class PostgresConnectionStringsPage extends DashboardPage { new InputKeyValue('Python', `dbname='postgres' user='postgres' host='${endpoint.ip}' password='{your_password_here}' port='${endpoint.port}' sslmode='true'`), new InputKeyValue('Ruby', `host=${endpoint.ip}; dbname=postgres user=postgres password={your_password_here} port=${endpoint.port} sslmode=require`), new InputKeyValue('Web App', `Database=postgres; Data Source=${endpoint.ip}; User Id=postgres; Password={your_password_here}`) - ]); + ]; + } + + private handleServiceUpdated() { + this.keyValueContainer?.refresh(this.getConnectionStrings()); + this.loading!.loading = false; } } diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresDashboard.ts b/extensions/arc/src/ui/dashboards/postgres/postgresDashboard.ts index 756da5722c..62e30cb910 100644 --- a/extensions/arc/src/ui/dashboards/postgres/postgresDashboard.ts +++ b/extensions/arc/src/ui/dashboards/postgres/postgresDashboard.ts @@ -23,8 +23,10 @@ export class PostgresDashboard extends Dashboard { public async showDashboard(): Promise { await super.showDashboard(); + // Kick off the model refresh but don't wait on it since that's all handled with callbacks anyways - this._postgresModel.refresh().catch(err => console.log(`Error refreshing Postgres dashboard ${err}`)); + this._controllerModel.refresh().catch(err => console.log(`Error refreshing controller model for Postgres dashboard ${err}`)); + this._postgresModel.refresh().catch(err => console.log(`Error refreshing Postgres model for Postgres dashboard ${err}`)); } protected async registerTabs(modelView: azdata.ModelView): Promise<(azdata.DashboardTab | azdata.DashboardTabGroup)[]> { diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresOverviewPage.ts b/extensions/arc/src/ui/dashboards/postgres/postgresOverviewPage.ts index 0ff05ff3ff..67199fa692 100644 --- a/extensions/arc/src/ui/dashboards/postgres/postgresOverviewPage.ts +++ b/extensions/arc/src/ui/dashboards/postgres/postgresOverviewPage.ts @@ -29,26 +29,10 @@ export class PostgresOverviewPage extends DashboardPage { super(modelView); this.disposables.push( - this._controllerModel.onEndpointsUpdated( - () => this.eventuallyRunOnInitialized(() => this.refreshEndpoints()))); - - this.disposables.push( - this._controllerModel.onRegistrationsUpdated( - () => this.eventuallyRunOnInitialized(() => this.refreshProperties()))); - - this.disposables.push( - this._postgresModel.onServiceUpdated( - () => this.eventuallyRunOnInitialized(() => { - this.refreshProperties(); - this.refreshNodes(); - }))); - - this.disposables.push( - this._postgresModel.onPodsUpdated( - () => this.eventuallyRunOnInitialized(() => { - this.refreshProperties(); - this.refreshNodes(); - }))); + this._controllerModel.onEndpointsUpdated(() => this.eventuallyRunOnInitialized(() => this.handleEndpointsUpdated())), + this._controllerModel.onRegistrationsUpdated(() => this.eventuallyRunOnInitialized(() => this.handleRegistrationsUpdated())), + this._postgresModel.onServiceUpdated(() => this.eventuallyRunOnInitialized(() => this.hadleServiceUpdated())), + this._postgresModel.onPodsUpdated(() => this.eventuallyRunOnInitialized(() => this.handlePodsUpdated()))); } protected get title(): string { @@ -69,8 +53,17 @@ export class PostgresOverviewPage extends DashboardPage { root.addItem(content, { CSSStyles: { 'margin': '10px 20px 0px 20px' } }); // Properties - this.properties = this.modelView.modelBuilder.propertiesContainer().component(); - this.propertiesLoading = this.modelView.modelBuilder.loadingComponent().withItem(this.properties).component(); + this.properties = this.modelView.modelBuilder.propertiesContainer() + .withProperties({ + propertyItems: this.getProperties() + }).component(); + + this.propertiesLoading = this.modelView.modelBuilder.loadingComponent() + .withItem(this.properties) + .withProperties({ + loading: !this._controllerModel.registrationsLastUpdated && !this._postgresModel.serviceLastUpdated && !this._postgresModel.podsLastUpdated + }).component(); + content.addItem(this.propertiesLoading, { CSSStyles: cssStyles.text }); // Service endpoints @@ -80,10 +73,29 @@ export class PostgresOverviewPage extends DashboardPage { CSSStyles: titleCSS }).component()); - this.kibanaLink = this.modelView.modelBuilder.hyperlink().component(); - this.grafanaLink = this.modelView.modelBuilder.hyperlink().component(); - this.kibanaLoading = this.modelView.modelBuilder.loadingComponent().withItem(this.kibanaLink).component(); - this.grafanaLoading = this.modelView.modelBuilder.loadingComponent().withItem(this.grafanaLink).component(); + this.kibanaLink = this.modelView.modelBuilder.hyperlink() + .withProperties({ + label: this.getKibanaLink(), + url: this.getKibanaLink() + }).component(); + + this.grafanaLink = this.modelView.modelBuilder.hyperlink() + .withProperties({ + label: this.getGrafanaLink(), + url: this.getGrafanaLink() + }).component(); + + this.kibanaLoading = this.modelView.modelBuilder.loadingComponent() + .withItem(this.kibanaLink) + .withProperties({ + loading: !this._controllerModel.endpointsLastUpdated + }).component(); + + this.grafanaLoading = this.modelView.modelBuilder.loadingComponent() + .withItem(this.grafanaLink) + .withProperties({ + loading: !this._controllerModel.endpointsLastUpdated + }).component(); const endpointsTable = this.modelView.modelBuilder.declarativeTable().withProperties({ width: '100%', @@ -167,10 +179,15 @@ export class PostgresOverviewPage extends DashboardPage { rowCssStyles: cssStyles.tableRow } ], - data: [] + data: this.getNodes() }).component(); - this.nodesTableLoading = this.modelView.modelBuilder.loadingComponent().withItem(this.nodesTable).component(); + this.nodesTableLoading = this.modelView.modelBuilder.loadingComponent() + .withItem(this.nodesTable) + .withProperties({ + loading: !this._postgresModel.serviceLastUpdated && !this._postgresModel.podsLastUpdated + }).component(); + content.addItem(this.nodesTableLoading, { CSSStyles: { 'margin-bottom': '20px' } }); this.initialized = true; return root; @@ -300,11 +317,11 @@ export class PostgresOverviewPage extends DashboardPage { ]).component(); } - private refreshProperties() { + private getProperties(): azdata.PropertiesContainerItem[] { const registration = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace, this._postgresModel.name); const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint; - this.properties!.propertyItems = [ + return [ { displayName: loc.name, value: this._postgresModel.name }, { displayName: loc.coordinatorEndpoint, value: `postgresql://postgres@${endpoint.ip}:${endpoint.port}` }, { displayName: loc.status, value: this._postgresModel.service?.status?.state ?? '' }, @@ -314,29 +331,24 @@ export class PostgresOverviewPage extends DashboardPage { { displayName: loc.subscriptionId, value: registration?.subscriptionId ?? '' }, { displayName: loc.postgresVersion, value: this._postgresModel.service?.spec?.engine?.version?.toString() ?? '' } ]; - - this.propertiesLoading!.loading = false; } - private refreshEndpoints() { + private getKibanaLink(): string { const kibanaQuery = `kubernetes_namespace:"${this._postgresModel.namespace}" and cluster_name:"${this._postgresModel.name}"`; - const kibanaUrl = `${this._controllerModel.getEndpoint('logsui')?.endpoint}/app/kibana#/discover?_a=(query:(language:kuery,query:'${kibanaQuery}'))`; - this.kibanaLink!.label = kibanaUrl; - this.kibanaLink!.url = kibanaUrl; + return `${this._controllerModel.getEndpoint('logsui')?.endpoint}/app/kibana#/discover?_a=(query:(language:kuery,query:'${kibanaQuery}'))`; - const grafanaUrl = `${this._controllerModel.getEndpoint('metricsui')?.endpoint}/d/postgres-metrics?var-Namespace=${this._postgresModel.namespace}&var-Name=${this._postgresModel.name}`; - this.grafanaLink!.label = grafanaUrl; - this.grafanaLink!.url = grafanaUrl; - - this.kibanaLoading!.loading = false; - this.grafanaLoading!.loading = false; } - private refreshNodes() { + private getGrafanaLink(): string { + const grafanaQuery = `var-Namespace=${this._postgresModel.namespace}&var-Name=${this._postgresModel.name}`; + return `${this._controllerModel.getEndpoint('metricsui')?.endpoint}/d/postgres-metrics?${grafanaQuery}`; + } + + private getNodes(): string[][] { const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint; - this.nodesTable!.data = this._postgresModel.pods?.map((pod: V1Pod) => { - const name = pod.metadata?.name; + return this._postgresModel.pods?.map((pod: V1Pod) => { + const name = pod.metadata?.name ?? ''; const role: PodRole | undefined = PostgresModel.getPodRole(pod); const service = pod.metadata?.annotations?.['arcdata.microsoft.com/serviceHost']; const internalDns = service ? `${name}.${service}` : ''; @@ -348,7 +360,37 @@ export class PostgresOverviewPage extends DashboardPage { role === PodRole.Router ? `${endpoint.ip}:${endpoint.port}` : internalDns ]; }) ?? []; + } + private handleEndpointsUpdated() { + this.kibanaLink!.label = this.getKibanaLink(); + this.kibanaLink!.url = this.getKibanaLink(); + this.kibanaLoading!.loading = false; + + this.grafanaLink!.label = this.getGrafanaLink(); + this.grafanaLink!.url = this.getGrafanaLink(); + this.grafanaLoading!.loading = false; + } + + private handleRegistrationsUpdated() { + this.properties!.propertyItems = this.getProperties(); + this.propertiesLoading!.loading = false; + } + + private hadleServiceUpdated() { + this.properties!.propertyItems = this.getProperties(); + this.propertiesLoading!.loading = false; + + this.nodesTable!.data = this.getNodes(); + this.nodesTableLoading!.loading = false; + + } + + private handlePodsUpdated() { + this.properties!.propertyItems = this.getProperties(); + this.propertiesLoading!.loading = false; + + this.nodesTable!.data = this.getNodes(); this.nodesTableLoading!.loading = false; } } diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresPropertiesPage.ts b/extensions/arc/src/ui/dashboards/postgres/postgresPropertiesPage.ts index 9bde9492b1..92542b030a 100644 --- a/extensions/arc/src/ui/dashboards/postgres/postgresPropertiesPage.ts +++ b/extensions/arc/src/ui/dashboards/postgres/postgresPropertiesPage.ts @@ -7,22 +7,23 @@ import * as vscode from 'vscode'; import * as azdata from 'azdata'; import * as loc from '../../../localizedConstants'; import { IconPathHelper, cssStyles, ResourceType } from '../../../constants'; -import { KeyValueContainer, InputKeyValue, LinkKeyValue, TextKeyValue } from '../../components/keyValueContainer'; +import { KeyValueContainer, InputKeyValue, TextKeyValue, KeyValue } from '../../components/keyValueContainer'; import { DashboardPage } from '../../components/dashboardPage'; import { ControllerModel } from '../../../models/controllerModel'; import { PostgresModel } from '../../../models/postgresModel'; export class PostgresPropertiesPage extends DashboardPage { + private loading?: azdata.LoadingComponent; private keyValueContainer?: KeyValueContainer; constructor(protected modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) { super(modelView); this.disposables.push(this._postgresModel.onServiceUpdated( - () => this.eventuallyRunOnInitialized(() => this.refresh()))); + () => this.eventuallyRunOnInitialized(() => this.handleServiceUpdated()))); this.disposables.push(this._controllerModel.onRegistrationsUpdated( - () => this.eventuallyRunOnInitialized(() => this.refresh()))); + () => this.eventuallyRunOnInitialized(() => this.handleRegistrationsUpdated()))); } protected get title(): string { @@ -47,8 +48,14 @@ export class PostgresPropertiesPage extends DashboardPage { CSSStyles: { ...cssStyles.title, 'margin-bottom': '25px' } }).component()); - this.keyValueContainer = new KeyValueContainer(this.modelView.modelBuilder, []); - content.addItem(this.keyValueContainer.container); + this.keyValueContainer = new KeyValueContainer(this.modelView.modelBuilder, this.getProperties()); + this.loading = this.modelView.modelBuilder.loadingComponent() + .withItem(this.keyValueContainer.container) + .withProperties({ + loading: !this._postgresModel.serviceLastUpdated && !this._controllerModel.registrationsLastUpdated + }).component(); + + content.addItem(this.loading); this.initialized = true; return root; } @@ -63,6 +70,7 @@ export class PostgresPropertiesPage extends DashboardPage { refreshButton.onDidClick(async () => { refreshButton.enabled = false; try { + this.loading!.loading = true; await Promise.all([ this._postgresModel.refresh(), this._controllerModel.refresh() @@ -80,20 +88,31 @@ export class PostgresPropertiesPage extends DashboardPage { ]).component(); } - private refresh() { + private getProperties(): KeyValue[] { const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint; const connectionString = `postgresql://postgres@${endpoint.ip}:${endpoint.port}`; const registration = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace, this._postgresModel.name); - this.keyValueContainer?.refresh([ + return [ new InputKeyValue(loc.coordinatorEndpoint, connectionString), new InputKeyValue(loc.postgresAdminUsername, 'postgres'), new TextKeyValue(loc.status, this._postgresModel.service?.status?.state ?? 'Unknown'), - new LinkKeyValue(loc.dataController, this._controllerModel.namespace ?? '', _ => vscode.window.showInformationMessage('TODO: Go to data controller')), + // TODO: Make this a LinkKeyValue that opens the controller dashboard + new TextKeyValue(loc.dataController, this._controllerModel.namespace ?? ''), new TextKeyValue(loc.nodeConfiguration, this._postgresModel.configuration), new TextKeyValue(loc.postgresVersion, this._postgresModel.service?.spec?.engine?.version?.toString() ?? ''), new TextKeyValue(loc.resourceGroup, registration?.resourceGroupName ?? ''), new TextKeyValue(loc.subscriptionId, registration?.subscriptionId ?? '') - ]); + ]; + } + + private handleRegistrationsUpdated() { + this.keyValueContainer?.refresh(this.getProperties()); + this.loading!.loading = false; + } + + private handleServiceUpdated() { + this.keyValueContainer?.refresh(this.getProperties()); + this.loading!.loading = false; } } diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresResourceHealthPage.ts b/extensions/arc/src/ui/dashboards/postgres/postgresResourceHealthPage.ts index 3b68be566e..477189dd68 100644 --- a/extensions/arc/src/ui/dashboards/postgres/postgresResourceHealthPage.ts +++ b/extensions/arc/src/ui/dashboards/postgres/postgresResourceHealthPage.ts @@ -14,6 +14,8 @@ import { fromNow } from '../../../common/date'; export class PostgresResourceHealthPage extends DashboardPage { private interval: NodeJS.Timeout; private podsUpdated?: azdata.TextComponent; + private podsLoading?: azdata.LoadingComponent; + private conditionsLoading?: azdata.LoadingComponent; private podsTable?: azdata.DeclarativeTableComponent; private conditionsTable?: azdata.DeclarativeTableComponent; @@ -27,10 +29,10 @@ export class PostgresResourceHealthPage extends DashboardPage { })); this.disposables.push(this._postgresModel.onServiceUpdated( - () => this.eventuallyRunOnInitialized(() => this.refresh()))); + () => this.eventuallyRunOnInitialized(() => this.handleServiceUpdated()))); // Keep the last updated timestamps up to date with the current time - this.interval = setInterval(() => this.refresh(), 60 * 1000); + this.interval = setInterval(() => this.handleServiceUpdated(), 60 * 1000); } protected get title(): string { @@ -61,7 +63,10 @@ export class PostgresResourceHealthPage extends DashboardPage { CSSStyles: titleCSS }).component()); - this.podsUpdated = this.modelView.modelBuilder.text().component(); + this.podsUpdated = this.modelView.modelBuilder.text().withProperties({ + value: this.getPodsLastUpdated() + }).component(); + content.addItem(this.podsUpdated); // Pod overview @@ -84,9 +89,16 @@ export class PostgresResourceHealthPage extends DashboardPage { rowCssStyles: cssStyles.tableRow } ], - data: [] + data: this.getPodsTable() }).component(); - content.addItem(this.podsTable, { CSSStyles: { 'margin-bottom': '30px' } }); + + this.podsLoading = this.modelView.modelBuilder.loadingComponent() + .withItem(this.podsTable) + .withProperties({ + loading: !this._postgresModel.serviceLastUpdated + }).component(); + + content.addItem(this.podsLoading, { CSSStyles: { 'margin-bottom': '30px' } }); // Conditions table this.conditionsTable = this.modelView.modelBuilder.declarativeTable().withProperties({ @@ -125,10 +137,16 @@ export class PostgresResourceHealthPage extends DashboardPage { rowCssStyles: { ...cssStyles.tableRow, 'white-space': 'nowrap' } } ], - data: [] + data: this.getConditionsTable() }).component(); - content.addItem(this.conditionsTable); + this.conditionsLoading = this.modelView.modelBuilder.loadingComponent() + .withItem(this.conditionsTable) + .withProperties({ + loading: !this._postgresModel.serviceLastUpdated + }).component(); + + content.addItem(this.conditionsLoading); this.initialized = true; return root; } @@ -143,6 +161,8 @@ export class PostgresResourceHealthPage extends DashboardPage { refreshButton.onDidClick(async () => { refreshButton.enabled = false; try { + this.podsLoading!.loading = true; + this.conditionsLoading!.loading = true; await this._postgresModel.refresh(); } catch (error) { vscode.window.showErrorMessage(loc.refreshFailed(error)); @@ -156,17 +176,22 @@ export class PostgresResourceHealthPage extends DashboardPage { ]).component(); } - private refresh() { - this.podsUpdated!.value = loc.updated(fromNow(this._postgresModel.serviceLastUpdated!, true)); + private getPodsLastUpdated(): string { + return this._postgresModel.serviceLastUpdated + ? loc.updated(fromNow(this._postgresModel.serviceLastUpdated!, true)) : ''; + } - this.podsTable!.data = [ - [this._postgresModel.service?.status?.podsRunning, loc.running], - [this._postgresModel.service?.status?.podsPending, loc.pending], - [this._postgresModel.service?.status?.podsFailed, loc.failed], - [this._postgresModel.service?.status?.podsUnknown, loc.unknown] + private getPodsTable(): (string | number)[][] { + return [ + [this._postgresModel.service?.status?.podsRunning ?? 0, loc.running], + [this._postgresModel.service?.status?.podsPending ?? 0, loc.pending], + [this._postgresModel.service?.status?.podsFailed ?? 0, loc.failed], + [this._postgresModel.service?.status?.podsUnknown ?? 0, loc.unknown] ]; + } - this.conditionsTable!.data = this._postgresModel.service?.status?.conditions?.map(c => { + private getConditionsTable(): (string | azdata.ImageComponent)[][] { + return this._postgresModel.service?.status?.conditions?.map(c => { const healthy = c.type === 'Ready' ? c.status === 'True' : c.status === 'False'; const image = this.modelView.modelBuilder.image().withProperties({ @@ -178,11 +203,20 @@ export class PostgresResourceHealthPage extends DashboardPage { }).component(); return [ - c.type, + c.type ?? '', image, - c.message, - fromNow(c.lastTransitionTime!, true) + c.message ?? '', + c.lastTransitionTime ? fromNow(c.lastTransitionTime!, true) : '' ]; }) ?? []; } + + private handleServiceUpdated() { + this.podsUpdated!.value = this.getPodsLastUpdated(); + this.podsTable!.data = this.getPodsTable(); + this.podsLoading!.loading = false; + + this.conditionsTable!.data = this.getConditionsTable(); + this.conditionsLoading!.loading = false; + } }