Arc Postgres: Fixes for May release (#10877)

This commit is contained in:
Brian Bergeron
2020-06-11 14:55:33 -07:00
committed by GitHub
parent 6b86cbdd6e
commit a099b9e72a
126 changed files with 5815 additions and 138 deletions

View File

@@ -5,19 +5,29 @@
import * as vscode from 'vscode';
import * as loc from '../localizedConstants';
import { DuskyObjectModelsDatabaseService, DatabaseRouterApi, DuskyObjectModelsDatabase, V1Status } from '../controller/generated/dusky/api';
import { DuskyObjectModelsDatabaseService, DatabaseRouterApi, DuskyObjectModelsDatabase, V1Status, V1Pod } from '../controller/generated/dusky/api';
import { Authentication } from '../controller/auth';
export enum PodRole {
Monitor,
Router,
Shard
}
export class PostgresModel {
private _databaseRouter: DatabaseRouterApi;
private _service?: DuskyObjectModelsDatabaseService;
private _password?: string;
private _pods?: V1Pod[];
private readonly _onServiceUpdated = new vscode.EventEmitter<DuskyObjectModelsDatabaseService>();
private readonly _onPasswordUpdated = new vscode.EventEmitter<string>();
private readonly _onPodsUpdated = new vscode.EventEmitter<V1Pod[]>();
public onServiceUpdated = this._onServiceUpdated.event;
public onPasswordUpdated = this._onPasswordUpdated.event;
public onPodsUpdated = this._onPodsUpdated.event;
public serviceLastUpdated?: Date;
public passwordLastUpdated?: Date;
public podsLastUpdated?: Date;
constructor(controllerUrl: string, auth: Authentication, private _namespace: string, private _name: string) {
this._databaseRouter = new DatabaseRouterApi(controllerUrl);
@@ -49,6 +59,11 @@ export class PostgresModel {
return this._password;
}
/** Returns the service's pods */
public pods(): V1Pod[] | undefined {
return this._pods;
}
/** Refreshes the model */
public async refresh() {
await Promise.all([
@@ -57,10 +72,15 @@ export class PostgresModel {
this.serviceLastUpdated = new Date();
this._onServiceUpdated.fire(this._service);
}),
this._databaseRouter.getDuskyPassword(this._namespace, this._name).then(async response => {
this._databaseRouter.getDuskyPassword(this._namespace, this._name).then(response => {
this._password = response.body;
this.passwordLastUpdated = new Date();
this._onPasswordUpdated.fire(this._password!);
}),
this._databaseRouter.getDuskyPods(this._namespace, this._name).then(response => {
this._pods = response.body;
this.podsLastUpdated = new Date();
this._onPodsUpdated.fire(this._pods!);
})
]);
}
@@ -91,13 +111,6 @@ export class PostgresModel {
return await (await this._databaseRouter.createDuskyDatabase(this.namespace(), this.name(), db)).body;
}
/** Returns the number of nodes in the service */
public numNodes(): number {
let nodes = this._service?.spec.scale?.shards ?? 1;
if (nodes > 1) { nodes++; } // for multiple shards there is an additional node for the coordinator
return nodes;
}
/**
* Returns the IP address and port of the service, preferring external IP over
* internal IP. If either field is not available it will be set to undefined.
@@ -115,23 +128,77 @@ export class PostgresModel {
/** Returns the service's configuration e.g. '3 nodes, 1.5 vCores, 1GiB RAM, 2GiB storage per node' */
public configuration(): string {
const nodes = this.numNodes();
const cpuLimit = this._service?.spec.scheduling?.resources?.limits?.['cpu'];
const ramLimit = this._service?.spec.scheduling?.resources?.limits?.['memory'];
const cpuRequest = this._service?.spec.scheduling?.resources?.requests?.['cpu'];
const ramRequest = this._service?.spec.scheduling?.resources?.requests?.['memory'];
const storage = this._service?.spec.storage.volumeSize;
// TODO: Resource requests and limits can be configured per role. Figure out how
// to display that in the UI. For now, only show the default configuration.
const cpuLimit = this._service?.spec?.scheduling?._default?.resources?.limits?.['cpu'];
const ramLimit = this._service?.spec?.scheduling?._default?.resources?.limits?.['memory'];
const cpuRequest = this._service?.spec?.scheduling?._default?.resources?.requests?.['cpu'];
const ramRequest = this._service?.spec?.scheduling?._default?.resources?.requests?.['memory'];
const storage = this._service?.spec?.storage?.volumeSize;
const nodes = this.pods()?.length;
let configuration: string[] = [];
if (nodes) {
configuration.push(`${nodes} ${nodes > 1 ? loc.nodes : loc.node}`);
}
// Prefer limits if they're provided, otherwise use requests if they're provided
let configuration = `${nodes} ${nodes > 1 ? loc.nodes : loc.node}`;
if (cpuLimit || cpuRequest) {
configuration += `, ${this.formatCores(cpuLimit ?? cpuRequest!)} ${loc.vCores}`;
configuration.push(`${this.formatCores(cpuLimit ?? cpuRequest!)} ${loc.vCores}`);
}
if (ramLimit || ramRequest) {
configuration += `, ${this.formatMemory(ramLimit ?? ramRequest!)} ${loc.ram}`;
configuration.push(`${this.formatMemory(ramLimit ?? ramRequest!)} ${loc.ram}`);
}
if (storage) { configuration += `, ${storage} ${loc.storagePerNode}`; }
return configuration;
if (storage) {
configuration.push(`${this.formatMemory(storage)} ${loc.storagePerNode}`);
}
return configuration.join(', ');
}
/** Given a V1Pod, returns its PodRole or undefined if the role isn't known */
public static getPodRole(pod: V1Pod): PodRole | undefined {
const name = pod.metadata?.name;
const role = name?.substring(name.lastIndexOf('-'))[1];
switch (role) {
case 'm': return PodRole.Monitor;
case 'r': return PodRole.Router;
case 's': return PodRole.Shard;
default: return undefined;
}
}
/** Given a PodRole, returns its localized name */
public static getPodRoleName(role?: PodRole): string {
switch (role) {
case PodRole.Monitor: return loc.monitor;
case PodRole.Router: return loc.coordinator;
case PodRole.Shard: return loc.worker;
default: return '';
}
}
/** Given a V1Pod returns its status */
public static getPodStatus(pod: V1Pod) {
const phase = pod.status?.phase;
if (phase !== 'Running') {
return phase;
}
// Pods can be in the running phase while some
// containers are crashing, so check those too.
for (let c of pod.status?.containerStatuses?.filter(c => !c.ready) ?? []) {
const wReason = c.state?.waiting?.reason;
const tReason = c.state?.terminated?.reason;
if (wReason) { return wReason; }
if (tReason) { return tReason; }
}
return loc.running;
}
/**