The change to tear down RPC connections after the program exits -- to fix problems on Linux presumably due to the way libuv is implemented -- unfortunately introduces nondeterminism and overzealous termination that can happen at inopportune times. Instead, we need to wait for the current RPC queue to drain. To fix this, we'll maintain a list of currently active RPC calls and, only once they have completed, will we close the clients.
80 lines
2.8 KiB
TypeScript
80 lines
2.8 KiB
TypeScript
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
|
|
|
import { debuggablePromise } from "./debuggable";
|
|
|
|
// configured is set to true once configuration has been set.
|
|
let configured: boolean;
|
|
|
|
// monitor is a live connection to the resource monitor connection.
|
|
// IDEA: it would be nice to mirror the Protobuf structures as TypeScript interfaces.
|
|
let monitor: Object | undefined;
|
|
|
|
// engine is an optional live connection to the engine. This can be used for logging, etc.
|
|
let engine: Object | undefined;
|
|
|
|
// dryRun tells us whether we're performing a plan (true) versus a real deployment (false).
|
|
export let dryRun: boolean;
|
|
|
|
// hasMonitor returns true if we are currently connected to a resource monitoring service.
|
|
export function hasMonitor(): boolean {
|
|
return !!monitor;
|
|
}
|
|
|
|
// isDryRun returns true if we are planning.
|
|
export function isDryRun(): boolean {
|
|
return dryRun;
|
|
}
|
|
|
|
// getMonitor returns the current resource monitoring service client for RPC communications.
|
|
export function getMonitor(): Object | undefined {
|
|
if (!configured) {
|
|
configured = true;
|
|
console.warn("warning: Pulumi Fabric monitor is missing; no resources will be created");
|
|
}
|
|
return monitor;
|
|
}
|
|
|
|
// getEngine returns the current engine, if any, for RPC communications back to the resource engine.
|
|
export function getEngine(): Object | undefined {
|
|
return engine;
|
|
}
|
|
|
|
// configure initializes the current resource monitor and engine RPC connections, and whether we are performing a "dry
|
|
// run" (plan), versus a real deployment. It may only be called once.
|
|
export function configure(m: Object | undefined, e: Object | undefined, dr: boolean): void {
|
|
if (configured) {
|
|
throw new Error("Cannot configure runtime settings more than once");
|
|
}
|
|
monitor = m;
|
|
engine = e;
|
|
dryRun = dr;
|
|
configured = true;
|
|
}
|
|
|
|
// disconnect permanently disconnects from the server, closing the connections. It waits for the existing RPC
|
|
// queue to drain. If any RPCs come in afterwards, however, they will crash the process.
|
|
export function disconnect(): void {
|
|
debuggablePromise(rpcDone.then(() => {
|
|
if (monitor) {
|
|
(<any>monitor).close();
|
|
monitor = undefined;
|
|
}
|
|
if (engine) {
|
|
(<any>engine).close();
|
|
engine = undefined;
|
|
}
|
|
}));
|
|
}
|
|
|
|
// rpcDone resolves when the last known client-side RPC call finishes.
|
|
let rpcDone: Promise<any> = Promise.resolve();
|
|
|
|
// rpcKeepAlive registers a pending call to ensure that we don't prematurely disconnect from the server. It returns
|
|
// a function that, when invoked, signals that the RPC has completed.
|
|
export function rpcKeepAlive(): () => void {
|
|
let done: (() => void) | undefined = undefined;
|
|
let donePromise = debuggablePromise(new Promise<void>((resolve) => { done = resolve; }));
|
|
rpcDone = rpcDone.then(() => donePromise);
|
|
return done!;
|
|
}
|
|
|