Hook up MIAA dashboard overview (#10890)

* Hook up MIAA dashboard overview

* Fix merge conflicts

* Fix links

* Remove extra &
This commit is contained in:
Charles Gagnon
2020-06-15 11:25:31 -07:00
committed by GitHub
parent ff8b03aa5e
commit d9e70731f4
16 changed files with 335 additions and 126 deletions

View File

@@ -9,7 +9,7 @@ import * as loc from '../../../localizedConstants';
import { DashboardPage } from '../../components/dashboardPage';
import { IconPathHelper, cssStyles } from '../../../constants';
import { ControllerModel } from '../../../models/controllerModel';
import { resourceTypeToDisplayName, ResourceType } from '../../../common/utils';
import { resourceTypeToDisplayName, ResourceType, getAzurecoreApi } from '../../../common/utils';
import { RegistrationResponse } from '../../../controller/generated/v1/model/registrationResponse';
export class ControllerDashboardOverviewPage extends DashboardPage {
@@ -21,7 +21,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
super(modelView);
this._controllerModel.onRegistrationsUpdated((_: RegistrationResponse[]) => {
this.eventuallyRunOnInitialized(() => {
this.handleRegistrationsUpdated();
this.handleRegistrationsUpdated().catch(e => console.log(e));
});
});
this.refresh().catch(e => {
@@ -141,7 +141,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
).component();
}
private handleRegistrationsUpdated(): void {
private async handleRegistrationsUpdated(): Promise<void> {
const reg = this._controllerModel.controllerRegistration;
if (reg) {
this._propertiesContainer.propertyItems = [
@@ -155,7 +155,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
},
{
displayName: loc.region,
value: reg.location || '-'
value: (await getAzurecoreApi()).getRegionDisplayName(reg.location) || '-'
},
{
displayName: loc.subscriptionId,
@@ -180,7 +180,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
];
}
this._arcResourcesTable.data = this._controllerModel.registrations()
this._arcResourcesTable.data = this._controllerModel.registrations
.filter(r => r.instanceType !== ResourceType.dataControllers)
.map(r => [r.instanceName, resourceTypeToDisplayName(r.instanceType), r.vCores]);
}

View File

@@ -18,7 +18,7 @@ export class MiaaDashboard extends Dashboard {
}
protected async registerTabs(modelView: azdata.ModelView): Promise<(azdata.DashboardTab | azdata.DashboardTabGroup)[]> {
const overviewPage = new MiaaDashboardOverviewPage(modelView, this._controllerModel);
const overviewPage = new MiaaDashboardOverviewPage(modelView, this._controllerModel, this._miaaModel);
const connectionStringsPage = new MiaaConnectionStringsPage(modelView, this._controllerModel, this._miaaModel);
return [
overviewPage.tab,

View File

@@ -6,16 +6,48 @@
import * as azdata from 'azdata';
import * as loc from '../../../localizedConstants';
import { DashboardPage } from '../../components/dashboardPage';
import { IconPathHelper } from '../../../constants';
import { ControllerModel } from '../../../models/controllerModel';
import { resourceTypeToDisplayName } from '../../../common/utils';
import { IconPathHelper, cssStyles } from '../../../constants';
import { ControllerModel, Registration } from '../../../models/controllerModel';
import { ResourceType, getAzurecoreApi } from '../../../common/utils';
import { MiaaModel, DatabaseModel } from '../../../models/miaaModel';
import { HybridSqlNsNameGetResponse } from '../../../controller/generated/v1/model/hybridSqlNsNameGetResponse';
import { EndpointModel } from '../../../controller/generated/v1/api';
export class MiaaDashboardOverviewPage extends DashboardPage {
private _arcResourcesTable!: azdata.DeclarativeTableComponent;
private _propertiesLoading!: azdata.LoadingComponent;
private _kibanaLoading!: azdata.LoadingComponent;
private _grafanaLoading!: azdata.LoadingComponent;
private _databasesTableLoading!: azdata.LoadingComponent;
constructor(modelView: azdata.ModelView, private _controllerModel: ControllerModel) {
private _propertiesContainer!: azdata.PropertiesContainerComponent;
private _kibanaLink!: azdata.HyperlinkComponent;
private _grafanaLink!: azdata.HyperlinkComponent;
private _databasesTable!: azdata.DeclarativeTableComponent;
private _instanceProperties = {
resourceGroup: '-',
status: '-',
dataController: '-',
region: '-',
subscriptionId: '-',
miaaAdmin: '-',
host: '-',
computeAndStorage: '-'
};
constructor(modelView: azdata.ModelView, private _controllerModel: ControllerModel, private _miaaModel: MiaaModel) {
super(modelView);
this._instanceProperties.miaaAdmin = this._miaaModel.connectionProfile.userName;
this._controllerModel.onRegistrationsUpdated((_: Registration[]) => {
this.eventuallyRunOnInitialized(() => {
this.handleRegistrationsUpdated().catch(e => console.log(e));
});
});
this._controllerModel.onEndpointsUpdated(endpoints => this.eventuallyRunOnInitialized(() => this.handleEndpointsUpdated(endpoints)));
this._miaaModel.onStatusUpdated(status => this.eventuallyRunOnInitialized(() => this.handleMiaaStatusUpdated(status)));
this._miaaModel.onDatabasesUpdated(databases => this.eventuallyRunOnInitialized(() => this.handleDatabasesUpdated(databases)));
this.refresh().catch(e => {
console.log(e);
});
@@ -34,8 +66,7 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
}
protected async refresh(): Promise<void> {
await this._controllerModel.refresh();
this.eventuallyRunOnInitialized(() => this._arcResourcesTable.data = this._controllerModel.registrations().map(r => [r.instanceName, resourceTypeToDisplayName(r.instanceType), r.vCores]));
await Promise.all([this._controllerModel.refresh(), this._miaaModel.refresh()]);
}
public get container(): azdata.Component {
@@ -45,76 +76,90 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
.withProperties({ CSSStyles: { 'margin': '18px' } })
.component();
const propertiesContainer = this.modelView.modelBuilder.propertiesContainer().withProperties<azdata.PropertiesContainerComponentProperties>({
propertyItems: [
{
displayName: loc.resourceGroup,
value: 'contosoRG123'
},
{
displayName: loc.region,
value: 'West US'
},
{
displayName: loc.subscription,
value: 'contososub5678'
},
{
displayName: loc.subscriptionId,
value: '88abe223-c630-4f2c-8782-00bb5be874f6'
},
{
displayName: loc.state,
value: 'Connected'
},
{
displayName: loc.host,
value: 'plainscluster.sqlarcdm.database.windows.net'
}
]
}).component();
// Properties
this._propertiesContainer = this.modelView.modelBuilder.propertiesContainer().component();
this._propertiesLoading = this.modelView.modelBuilder.loadingComponent().withItem(this._propertiesContainer).component();
rootContainer.addItem(this._propertiesLoading, { CSSStyles: cssStyles.text });
rootContainer.addItem(propertiesContainer);
// Service endpoints
const titleCSS = { ...cssStyles.title, 'margin-block-start': '2em', 'margin-block-end': '0' };
rootContainer.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.serviceEndpoints, CSSStyles: titleCSS }).component());
const arcResourcesTitle = this.modelView.modelBuilder.text()
.withProperties<azdata.TextComponentProperties>({ value: loc.arcResources })
.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();
rootContainer.addItem(arcResourcesTitle, {
CSSStyles: {
'font-size': '14px'
}
});
this._arcResourcesTable = this.modelView.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
data: [],
const endpointsTable = this.modelView.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
width: '100%',
columns: [
{
displayName: loc.name,
valueType: azdata.DeclarativeDataType.string,
width: '33%',
isReadOnly: true
}, {
displayName: loc.type,
isReadOnly: true,
width: '20%',
headerCssStyles: cssStyles.tableHeader,
rowCssStyles: cssStyles.tableRow
},
{
displayName: loc.endpoint,
valueType: azdata.DeclarativeDataType.component,
isReadOnly: true,
width: '50%',
headerCssStyles: cssStyles.tableHeader,
rowCssStyles: {
...cssStyles.tableRow,
'overflow': 'hidden',
'text-overflow': 'ellipsis',
'white-space': 'nowrap',
'max-width': '0'
}
},
{
displayName: loc.description,
valueType: azdata.DeclarativeDataType.string,
width: '33%',
isReadOnly: true
}, {
displayName: loc.computeAndStorage,
valueType: azdata.DeclarativeDataType.string,
width: '34%',
isReadOnly: true
isReadOnly: true,
width: '30%',
headerCssStyles: cssStyles.tableHeader,
rowCssStyles: cssStyles.tableRow
}
],
width: '100%',
ariaLabel: loc.arcResources
data: [
[loc.kibanaDashboard, this._kibanaLoading, loc.kibanaDashboardDescription],
[loc.grafanaDashboard, this._grafanaLoading, loc.grafanaDashboardDescription]]
}).component();
const arcResourcesTableContainer = this.modelView.modelBuilder.divContainer()
.withItems([this._arcResourcesTable])
.component();
rootContainer.addItem(endpointsTable);
// Databases
rootContainer.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.databases, CSSStyles: titleCSS }).component());
this._databasesTable = this.modelView.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
width: '100%',
columns: [
{
displayName: loc.name,
valueType: azdata.DeclarativeDataType.string,
isReadOnly: true,
width: '80%',
headerCssStyles: cssStyles.tableHeader,
rowCssStyles: cssStyles.tableRow
},
{
displayName: loc.status,
valueType: azdata.DeclarativeDataType.string,
isReadOnly: true,
width: '20%',
headerCssStyles: cssStyles.tableHeader,
rowCssStyles: cssStyles.tableRow
}
],
data: []
}).component();
this._databasesTableLoading = this.modelView.modelBuilder.loadingComponent().withItem(this._databasesTable).component();
this._databasesTableLoading.loading = false;
rootContainer.addItem(this._databasesTableLoading, { CSSStyles: { 'margin-bottom': '20px' } });
rootContainer.addItem(arcResourcesTableContainer);
this.initialized = true;
return rootContainer;
}
@@ -151,4 +196,80 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
).component();
}
private async handleRegistrationsUpdated(): Promise<void> {
const reg = this._controllerModel.getRegistration(ResourceType.sqlManagedInstances, this._miaaModel.namespace, this._miaaModel.name);
if (reg) {
this._instanceProperties.resourceGroup = reg.resourceGroupName || '-';
this._instanceProperties.dataController = this._controllerModel.controllerRegistration?.instanceName || '-';
this._instanceProperties.region = (await getAzurecoreApi()).getRegionDisplayName(reg.location);
this._instanceProperties.subscriptionId = reg.subscriptionId || '-';
this._instanceProperties.computeAndStorage = reg.vCores || '-';
this._instanceProperties.host = reg.externalEndpoint || '-';
this.refreshDisplayedProperties();
}
}
private async handleMiaaStatusUpdated(status: HybridSqlNsNameGetResponse): Promise<void> {
this._instanceProperties.status = status.status || '-';
this.refreshDisplayedProperties();
}
private handleEndpointsUpdated(endpoints: EndpointModel[]): void {
const kibanaQuery = `kubernetes_namespace:"${this._miaaModel.namespace}" and instance_name :"${this._miaaModel.name}"`;
const kibanaUrl = `${endpoints.find(e => e.name === 'logsui')?.endpoint}/app/kibana#/discover?_a=(query:(language:kuery,query:'${kibanaQuery}'))`;
this._kibanaLink.label = kibanaUrl;
this._kibanaLink.url = kibanaUrl;
const grafanaUrl = `${endpoints.find(e => e.name === 'metricsui')?.endpoint}/d/wZx3OUdmz/azure-sql-db-managed-instance-metrics?var-hostname=${this._miaaModel.name}-0`;
this._grafanaLink.label = grafanaUrl;
this._grafanaLink.url = grafanaUrl;
this._kibanaLoading!.loading = false;
this._grafanaLoading!.loading = false;
}
private handleDatabasesUpdated(databases: DatabaseModel[]): void {
this._databasesTable.data = databases.map(d => [d.name, d.status]);
this._databasesTableLoading.loading = false;
}
private refreshDisplayedProperties(): void {
this._propertiesContainer.propertyItems = [
{
displayName: loc.resourceGroup,
value: this._instanceProperties.resourceGroup
},
{
displayName: loc.status,
value: this._instanceProperties.status
},
{
displayName: loc.dataController,
value: this._instanceProperties.dataController
},
{
displayName: loc.region,
value: this._instanceProperties.region
},
{
displayName: loc.subscriptionId,
value: this._instanceProperties.subscriptionId
},
{
displayName: loc.miaaAdmin,
value: this._instanceProperties.miaaAdmin
},
{
displayName: loc.host,
value: this._instanceProperties.host
},
{
displayName: loc.computeAndStorage,
value: this._instanceProperties.computeAndStorage
}
];
this._propertiesLoading.loading = false;
}
}

View File

@@ -85,7 +85,7 @@ export class PostgresConnectionStringsPage extends DashboardPage {
}
private refresh() {
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint();
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint;
this.keyValueContainer?.refresh([
new InputKeyValue('ADO.NET', `Server=${endpoint.ip};Database=postgres;Port=${endpoint.port};User Id=postgres;Password={your_password_here};Ssl Mode=Require;`),

View File

@@ -49,8 +49,8 @@ export class PostgresDiagnoseAndSolveProblemsPage extends DashboardPage {
}).component();
troubleshootButton.onDidClick(() => {
process.env['POSTGRES_SERVER_NAMESPACE'] = this._postgresModel.namespace();
process.env['POSTGRES_SERVER_NAME'] = this._postgresModel.name();
process.env['POSTGRES_SERVER_NAMESPACE'] = this._postgresModel.namespace;
process.env['POSTGRES_SERVER_NAME'] = this._postgresModel.name;
vscode.commands.executeCommand('bookTreeView.openBook', this._context.asAbsolutePath('notebooks/arc'), true, 'postgres/tsg100-troubleshoot-postgres');
});

View File

@@ -204,9 +204,9 @@ export class PostgresOverviewPage extends DashboardPage {
s.arc = s.arc ?? new DuskyObjectModelsDatabaseServiceArcPayload();
s.arc.servicePassword = password;
});
vscode.window.showInformationMessage(loc.passwordReset(this._postgresModel.fullName()));
vscode.window.showInformationMessage(loc.passwordReset(this._postgresModel.fullName));
} catch (error) {
vscode.window.showErrorMessage(loc.passwordResetFailed(this._postgresModel.fullName(), error));
vscode.window.showErrorMessage(loc.passwordResetFailed(this._postgresModel.fullName, error));
} finally {
resetPasswordButton.enabled = true;
}
@@ -222,13 +222,13 @@ export class PostgresOverviewPage extends DashboardPage {
deleteButton.enabled = false;
try {
const response = await vscode.window.showQuickPick([loc.yes, loc.no], {
placeHolder: loc.deleteServicePrompt(this._postgresModel.fullName())
placeHolder: loc.deleteServicePrompt(this._postgresModel.fullName)
});
if (response !== loc.yes) { return; }
await this._postgresModel.delete();
vscode.window.showInformationMessage(loc.serviceDeleted(this._postgresModel.fullName()));
vscode.window.showInformationMessage(loc.serviceDeleted(this._postgresModel.fullName));
} catch (error) {
vscode.window.showErrorMessage(loc.serviceDeletionFailed(this._postgresModel.fullName(), error));
vscode.window.showErrorMessage(loc.serviceDeletionFailed(this._postgresModel.fullName, error));
} finally {
deleteButton.enabled = true;
}
@@ -267,9 +267,9 @@ export class PostgresOverviewPage extends DashboardPage {
}).component();
openInAzurePortalButton.onDidClick(async () => {
const r = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace(), this._postgresModel.name());
const r = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace, this._postgresModel.name);
if (!r) {
vscode.window.showErrorMessage(loc.couldNotFindAzureResource(this._postgresModel.fullName()));
vscode.window.showErrorMessage(loc.couldNotFindAzureResource(this._postgresModel.fullName));
} else {
vscode.env.openExternal(vscode.Uri.parse(
`https://portal.azure.com/#resource/subscriptions/${r.subscriptionId}/resourceGroups/${r.resourceGroupName}/providers/Microsoft.AzureData/${ResourceType.postgresInstances}/${r.instanceName}`));
@@ -286,30 +286,30 @@ export class PostgresOverviewPage extends DashboardPage {
}
private refreshProperties() {
const registration = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace(), this._postgresModel.name());
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint();
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 = [
{ displayName: loc.name, value: this._postgresModel.name() },
{ displayName: loc.coordinatorEndpoint, value: `postgresql://postgres:${this._postgresModel.password()}@${endpoint.ip}:${endpoint.port}` },
{ displayName: loc.status, value: this._postgresModel.service()?.status?.state ?? '' },
{ displayName: loc.name, value: this._postgresModel.name },
{ displayName: loc.coordinatorEndpoint, value: `postgresql://postgres:${this._postgresModel.password}@${endpoint.ip}:${endpoint.port}` },
{ displayName: loc.status, value: this._postgresModel.service?.status?.state ?? '' },
{ displayName: loc.postgresAdminUsername, value: 'postgres' },
{ displayName: loc.dataController, value: this._controllerModel?.namespace() ?? '' },
{ displayName: loc.nodeConfiguration, value: this._postgresModel.configuration() },
{ displayName: loc.dataController, value: this._controllerModel?.namespace ?? '' },
{ displayName: loc.nodeConfiguration, value: this._postgresModel.configuration },
{ displayName: loc.subscriptionId, value: registration?.subscriptionId ?? '' },
{ displayName: loc.postgresVersion, value: this._postgresModel.service()?.spec?.engine?.version?.toString() ?? '' }
{ displayName: loc.postgresVersion, value: this._postgresModel.service?.spec?.engine?.version?.toString() ?? '' }
];
this.propertiesLoading!.loading = false;
}
private refreshEndpoints() {
const kibanaQuery = `kubernetes_namespace:"${this._postgresModel.namespace()}" and cluster_name:"${this._postgresModel.name()}"`;
const kibanaUrl = `${this._controllerModel.endpoint('logsui')?.endpoint}/app/kibana#/discover?_a=(query:(language:kuery,query:'${kibanaQuery}'))`;
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;
const grafanaUrl = `${this._controllerModel.endpoint('metricsui')?.endpoint}/d/postgres-metrics?var-Namespace=${this._postgresModel.namespace()}&var-Name=${this._postgresModel.name()}`;
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;
@@ -318,9 +318,9 @@ export class PostgresOverviewPage extends DashboardPage {
}
private refreshNodes() {
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint();
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint;
this.nodesTable!.data = this._postgresModel.pods()?.map((pod: V1Pod) => {
this.nodesTable!.data = 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'];

View File

@@ -78,17 +78,17 @@ export class PostgresPropertiesPage extends DashboardPage {
}
private refresh() {
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint();
const connectionString = `postgresql://postgres:${this._postgresModel.password()}@${endpoint.ip}:${endpoint.port}`;
const registration = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace(), this._postgresModel.name());
const endpoint: { ip?: string, port?: number } = this._postgresModel.endpoint;
const connectionString = `postgresql://postgres:${this._postgresModel.password}@${endpoint.ip}:${endpoint.port}`;
const registration = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace, this._postgresModel.name);
this.keyValueContainer?.refresh([
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')),
new LinkKeyValue(loc.nodeConfiguration, this._postgresModel.configuration(), _ => vscode.window.showInformationMessage('TODO: Go to configuration')),
new TextKeyValue(loc.postgresVersion, this._postgresModel.service()?.spec?.engine?.version?.toString() ?? ''),
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')),
new LinkKeyValue(loc.nodeConfiguration, this._postgresModel.configuration, _ => vscode.window.showInformationMessage('TODO: Go to configuration')),
new TextKeyValue(loc.postgresVersion, this._postgresModel.service?.spec?.engine?.version?.toString() ?? ''),
new TextKeyValue(loc.resourceGroup, registration?.resourceGroupName ?? ''),
new TextKeyValue(loc.subscriptionId, registration?.subscriptionId ?? '')
]);

View File

@@ -51,9 +51,9 @@ export class PostgresSupportRequestPage extends DashboardPage {
}).component();
supportRequestButton.onDidClick(() => {
const r = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace(), this._postgresModel.name());
const r = this._controllerModel.getRegistration(ResourceType.postgresInstances, this._postgresModel.namespace, this._postgresModel.name);
if (!r) {
vscode.window.showErrorMessage(loc.couldNotFindAzureResource(this._postgresModel.fullName()));
vscode.window.showErrorMessage(loc.couldNotFindAzureResource(this._postgresModel.fullName));
} else {
vscode.env.openExternal(vscode.Uri.parse(
`https://portal.azure.com/#resource/subscriptions/${r.subscriptionId}/resourceGroups/${r.resourceGroupName}/providers/Microsoft.AzureData/${ResourceType.postgresInstances}/${r.instanceName}/supportrequest`));