mirror of https://github.com/pulumi/pulumi.git
361 lines
10 KiB
TypeScript
361 lines
10 KiB
TypeScript
// Copyright 2016-2022, Pulumi Corporation.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
import { AsyncLocalStorage } from "async_hooks";
|
|
import { ICallbackServer } from "./callbacks";
|
|
import * as config from "./config";
|
|
import { Stack } from "./stack";
|
|
|
|
import * as engrpc from "../proto/engine_grpc_pb";
|
|
import * as resrpc from "../proto/resource_grpc_pb";
|
|
import type { ResourceModule, ResourcePackage } from "./rpc";
|
|
|
|
const nodeEnvKeys = {
|
|
project: "PULUMI_NODEJS_PROJECT",
|
|
stack: "PULUMI_NODEJS_STACK",
|
|
dryRun: "PULUMI_NODEJS_DRY_RUN",
|
|
queryMode: "PULUMI_NODEJS_QUERY_MODE",
|
|
parallel: "PULUMI_NODEJS_PARALLEL",
|
|
monitorAddr: "PULUMI_NODEJS_MONITOR",
|
|
engineAddr: "PULUMI_NODEJS_ENGINE",
|
|
syncDir: "PULUMI_NODEJS_SYNC",
|
|
|
|
// Unlike the values above, this value is not set by the CLI and is
|
|
// controlled via a user-set environment variable.
|
|
cacheDynamicProviders: "PULUMI_NODEJS_CACHE_DYNAMIC_PROVIDERS",
|
|
|
|
organization: "PULUMI_NODEJS_ORGANIZATION",
|
|
};
|
|
|
|
const pulumiEnvKeys = {
|
|
legacyApply: "PULUMI_ENABLE_LEGACY_APPLY",
|
|
};
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
export const asyncLocalStorage = new AsyncLocalStorage<Store>();
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
export interface WriteableOptions {
|
|
/**
|
|
* The name of the current project.
|
|
*/
|
|
project?: string;
|
|
|
|
/**
|
|
* The name of the current stack being deployed into.
|
|
*/
|
|
stack?: string;
|
|
|
|
/**
|
|
* The degree of parallelism for resource operations (default is serial).
|
|
*/
|
|
parallel?: number;
|
|
|
|
/**
|
|
* A connection string to the engine's RPC, in case we need to reestablish.
|
|
*/
|
|
engineAddr?: string;
|
|
|
|
/**
|
|
* A connection string to the monitor's RPC, in case we need to reestablish.
|
|
*/
|
|
monitorAddr?: string;
|
|
|
|
/**
|
|
* Whether we are performing a preview (true) or a real deployment (false).
|
|
*/
|
|
dryRun?: boolean;
|
|
|
|
/**
|
|
* True if we're in testing mode (allows execution without the CLI).
|
|
*/
|
|
testModeEnabled?: boolean;
|
|
|
|
/**
|
|
* True if we're in query mode (does not allow resource registration).
|
|
*/
|
|
queryMode?: boolean;
|
|
|
|
/**
|
|
* True if we will resolve missing outputs to inputs during preview.
|
|
*/
|
|
legacyApply?: boolean;
|
|
|
|
/**
|
|
* True if we will cache serialized dynamic providers on the program side.
|
|
*/
|
|
cacheDynamicProviders?: boolean;
|
|
|
|
/**
|
|
* The name of the current organization (if available).
|
|
*/
|
|
organization?: string;
|
|
|
|
/**
|
|
* The number of process listeners which can be registered before writing a
|
|
* warning.
|
|
*/
|
|
maximumProcessListeners: number;
|
|
|
|
/**
|
|
* A directory containing the send/receive files for making synchronous
|
|
* invokes to the engine.
|
|
*/
|
|
syncDir?: string;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
export interface Store {
|
|
settings: {
|
|
options: WriteableOptions;
|
|
monitor?: resrpc.IResourceMonitorClient;
|
|
engine?: engrpc.IEngineClient;
|
|
rpcDone: Promise<any>;
|
|
// Needed for legacy @pulumi/pulumi packages doing async feature checks.
|
|
featureSupport: Record<string, boolean>;
|
|
};
|
|
config: Record<string, string>;
|
|
stackResource?: Stack;
|
|
leakCandidates: Set<Promise<any>>;
|
|
logErrorCount: number;
|
|
|
|
/**
|
|
* Tells us if the resource monitor we are connected to is able to support
|
|
* secrets across its RPC interface. When it does, we marshal outputs marked
|
|
* with the secret bit in a special way.
|
|
*/
|
|
supportsSecrets: boolean;
|
|
|
|
/**
|
|
* Tells us if the resource monitor we are connected to is able to support
|
|
* resource references across its RPC interface. When it does, we marshal
|
|
* resources in a special way.
|
|
*/
|
|
supportsResourceReferences: boolean;
|
|
|
|
/**
|
|
* Tells u if the resource monitor we are connected to is able to support
|
|
* output values across its RPC interface. When it does, we marshal outputs
|
|
* in a special way.
|
|
*/
|
|
supportsOutputValues: boolean;
|
|
|
|
/**
|
|
* Tells us if the resource monitor we are connected to is able to support
|
|
* the `deletedWith` resource option across its RPC interface.
|
|
*/
|
|
supportsDeletedWith: boolean;
|
|
|
|
/**
|
|
* Tells us if the resource monitor we are connected to is able to support
|
|
* alias specs across its RPC interface. When it does, we marshal aliases in
|
|
* a special way.
|
|
*/
|
|
supportsAliasSpecs: boolean;
|
|
|
|
/**
|
|
* Tells us if the resource monitor we are connected to is able to support
|
|
* remote transforms across its RPC interface. When it does, we marshal
|
|
* transforms to the monitor instead of running them locally.
|
|
*/
|
|
supportsTransforms: boolean;
|
|
|
|
/**
|
|
* Tells us if the resource monitor we are connected to is able to support
|
|
* remote invoke transforms across its RPC interface. When it does, we marshal
|
|
* transforms to the monitor instead of running them locally.
|
|
*/
|
|
supportsInvokeTransforms: boolean;
|
|
|
|
/**
|
|
* Tells us if the resource monitor we are connected to is able to support
|
|
* package references and parameterized providers.
|
|
*/
|
|
supportsParameterization: boolean;
|
|
|
|
/**
|
|
* The callback service running for this deployment. This registers
|
|
* callbacks and forwards them to the engine.
|
|
*/
|
|
callbacks?: ICallbackServer;
|
|
|
|
/**
|
|
* Tracks the list of resource packages that have been registered.
|
|
*/
|
|
resourcePackages: Map<string, ResourcePackage[]>;
|
|
|
|
/**
|
|
* Tracks the list of resource modules that have been registered.
|
|
*/
|
|
resourceModules: Map<string, ResourceModule[]>;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
export class LocalStore implements Store {
|
|
settings = {
|
|
options: {
|
|
organization: process.env[nodeEnvKeys.organization],
|
|
project: process.env[nodeEnvKeys.project] || "project",
|
|
stack: process.env[nodeEnvKeys.stack] || "stack",
|
|
dryRun: process.env[nodeEnvKeys.dryRun] === "true",
|
|
queryMode: process.env[nodeEnvKeys.queryMode] === "true",
|
|
monitorAddr: process.env[nodeEnvKeys.monitorAddr],
|
|
engineAddr: process.env[nodeEnvKeys.engineAddr],
|
|
syncDir: process.env[nodeEnvKeys.syncDir],
|
|
cacheDynamicProviders: process.env[nodeEnvKeys.cacheDynamicProviders] !== "false",
|
|
legacyApply: process.env[pulumiEnvKeys.legacyApply] === "true",
|
|
maximumProcessListeners: 30,
|
|
},
|
|
rpcDone: Promise.resolve(),
|
|
featureSupport: {},
|
|
};
|
|
config = {
|
|
[config.configEnvKey]: process.env[config.configEnvKey] || "",
|
|
[config.configSecretKeysEnvKey]: process.env[config.configSecretKeysEnvKey] || "",
|
|
};
|
|
stackResource = undefined;
|
|
|
|
/**
|
|
* Tracks the list of potential leak candidates.
|
|
*/
|
|
leakCandidates = new Set<Promise<any>>();
|
|
|
|
logErrorCount = 0;
|
|
|
|
supportsSecrets = false;
|
|
supportsResourceReferences = false;
|
|
supportsOutputValues = false;
|
|
supportsDeletedWith = false;
|
|
supportsAliasSpecs = false;
|
|
supportsTransforms = false;
|
|
supportsInvokeTransforms = false;
|
|
supportsParameterization = false;
|
|
resourcePackages = new Map<string, ResourcePackage[]>();
|
|
resourceModules = new Map<string, ResourceModule[]>();
|
|
}
|
|
|
|
/**
|
|
* Get the root stack resource for the current stack deployment.
|
|
*
|
|
* @internal
|
|
*/
|
|
export function getStackResource(): Stack | undefined {
|
|
const { stackResource } = getStore();
|
|
return stackResource;
|
|
}
|
|
|
|
/**
|
|
* Get the resource package map for the current stack deployment.
|
|
*
|
|
* @internal
|
|
*/
|
|
export function getResourcePackages(): Map<string, ResourcePackage[]> {
|
|
const store = getGlobalStore();
|
|
if (store.resourcePackages === undefined) {
|
|
// resourcePackages can be undefined if an older SDK where it was not defined is created it.
|
|
// In this case, we should initialize it to an empty map.
|
|
store.resourcePackages = new Map<string, ResourcePackage[]>();
|
|
}
|
|
return store.resourcePackages;
|
|
}
|
|
|
|
/**
|
|
* Get the resource module map for the current stack deployment.
|
|
*
|
|
* @internal
|
|
*/
|
|
export function getResourceModules(): Map<string, ResourceModule[]> {
|
|
const store = getGlobalStore();
|
|
if (store.resourceModules === undefined) {
|
|
// resourceModules can be undefined if an older SDK where it was not defined is created it.
|
|
// In this case, we should initialize it to an empty map.
|
|
store.resourceModules = new Map<string, ResourceModule[]>();
|
|
}
|
|
return store.resourceModules;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
export function setStackResource(newStackResource?: Stack) {
|
|
const localStore = getStore();
|
|
globalThis.stackResource = newStackResource;
|
|
localStore.stackResource = newStackResource;
|
|
}
|
|
|
|
declare global {
|
|
/* eslint-disable no-var */
|
|
var globalStore: Store;
|
|
var stackResource: Stack | undefined;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
export function getLocalStore(): Store | undefined {
|
|
return asyncLocalStorage.getStore();
|
|
}
|
|
|
|
(<any>getLocalStore).captureReplacement = () => {
|
|
const returnFunc = () => {
|
|
if (global.globalStore === undefined) {
|
|
global.globalStore = new LocalStore();
|
|
}
|
|
return global.globalStore;
|
|
};
|
|
return returnFunc;
|
|
};
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
export const getStore = () => {
|
|
const localStore = getLocalStore();
|
|
if (localStore === undefined) {
|
|
if (global.globalStore === undefined) {
|
|
global.globalStore = new LocalStore();
|
|
}
|
|
return global.globalStore;
|
|
}
|
|
return localStore;
|
|
};
|
|
|
|
(<any>getStore).captureReplacement = () => {
|
|
const returnFunc = () => {
|
|
if (global.globalStore === undefined) {
|
|
global.globalStore = new LocalStore();
|
|
}
|
|
return global.globalStore;
|
|
};
|
|
return returnFunc;
|
|
};
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
export const getGlobalStore = () => {
|
|
if (global.globalStore === undefined) {
|
|
global.globalStore = new LocalStore();
|
|
}
|
|
return global.globalStore;
|
|
};
|