Do not lazy initialize config or settings

The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.

In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.

Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.

So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
This commit is contained in:
Matt Ellis 2018-08-03 15:33:15 -07:00
parent 7b2a623ee8
commit 5eb78af779
6 changed files with 275 additions and 373 deletions

View file

@ -13,16 +13,7 @@
// limitations under the License.
// This is the entrypoint for running a Node.js program with minimal scaffolding.
import * as fs from "fs";
import * as minimist from "minimist";
import * as os from "os";
import * as path from "path";
import * as tsnode from "ts-node";
import * as util from "util";
import { RunError } from "../../errors";
import * as log from "../../log";
import * as runtime from "../../runtime";
function usage(): void {
console.error(`usage: RUN <flags> [program] <[arg]...>`);
@ -47,247 +38,54 @@ function printErrorUsageAndExit(message: string): never {
return process.exit(-1);
}
/**
* Attempts to provide a detailed error message for module load failure if the
* module that failed to load is the top-level module.
* @param program The name of the program given to `run`, i.e. the top level module
* @param error The error that occured. Must be a module load error.
*/
function reportModuleLoadFailure(program: string, error: Error): never {
// error is guaranteed to be a Node module load error. Node emits a very
// specific string in its error message for module load errors, which includes
// the module it was trying to load.
const errorRegex = /Cannot find module '(.*)'/;
// If there's no match, who knows what this exception is; it's not something
// we can provide an intelligent diagnostic for.
const moduleNameMatches = errorRegex.exec(error.message);
if (moduleNameMatches === null) {
throw error;
}
// Is the module that failed to load exactly the one that this script considered to
// be the top-level module for this program?
//
// We are only interested in producing good diagnostics for top-level module loads,
// since anything else are probably user code issues.
const moduleName = moduleNameMatches[1];
if (moduleName !== program) {
throw error;
}
console.error(`We failed to locate the entry point for your program: ${program}`);
// From here on out, we're going to try to inspect the program we're being asked to run
// a little to see what sort of details we can glean from it, in the hopes of producing
// a better error message.
//
// The first step of this is trying to slurp up a package.json for this program, if
// one exists.
const stat = fs.lstatSync(program);
let projectRoot: string;
if (stat.isDirectory()) {
projectRoot = program;
} else {
projectRoot = path.dirname(program);
}
let packageObject: Record<string, any>;
try {
const packageJson = path.join(projectRoot, "package.json");
packageObject = require(packageJson);
} catch {
// This is all best-effort so if we can't load the package.json file, that's
// fine.
return process.exit(1);
}
console.error("Here's what we think went wrong:");
// The objective here is to emit the best diagnostic we can, starting from the
// most specific to the least specific.
const deps = packageObject["dependencies"] || {};
const devDeps = packageObject["devDependencies"] || {};
const scripts = packageObject["scripts"] || {};
const mainProperty = packageObject["main"] || "index.js";
// Is there a build script associated with this program? It's a little confusing that the
// Pulumi CLI doesn't run build scripts before running the program so call that out
// explicitly.
if ("build" in scripts) {
const command = scripts["build"];
console.error(` * Your program looks like it has a build script associated with it ('${command}').\n`);
console.error("Pulumi does not run build scripts before running your program. " +
`Please run '${command}', 'yarn build', or 'npm run build' and try again.`);
return process.exit(1);
}
// Not all typescript programs have build scripts. If we think it's a typescript program,
// tell the user to run tsc.
if ("typescript" in deps || "typescript" in devDeps) {
console.error(" * Your program looks like a TypeScript program. Have you run 'tsc'?");
return process.exit(1);
}
// Not all projects are typescript. If there's a main property, check that the file exists.
if (mainProperty !== undefined && typeof mainProperty === "string") {
const mainFile = path.join(projectRoot, mainProperty);
if (!fs.existsSync(mainFile)) {
console.error(` * Your program's 'main' file (${mainFile}) does not exist.`);
return process.exit(1);
}
}
console.error(" * Yowzas, our sincere apologies, we haven't seen this before!");
console.error(` Here is the raw exception message we received: ${error.message}`);
return process.exit(1);
}
export function main(args: string[]): void {
function main(args: string[]): void {
// See usage above for the intended usage of this program, including flags and required args.
const config: {[key: string]: string} = {};
const argv: minimist.ParsedArgs = minimist(args, {
boolean: [ "dry-run" ],
string: [ "project", "stack", "parallel", "pwd", "monitor", "engine", "tracing" ],
unknown: (arg: string) => {
if (arg.indexOf("-") === 0) {
return printErrorUsageAndExit(`error: Unrecognized flag ${arg}`);
}
return true;
},
stopEarly: true,
});
// Load configuration passed from the language plugin
runtime.ensureConfig();
// If there is a --project=p, and/or a --stack=s, use them in the options.
const project: string | undefined = argv["project"];
const stack: string | undefined = argv["stack"];
// If there is a --pwd directive, switch directories.
const pwd: string | undefined = argv["pwd"];
if (pwd) {
process.chdir(pwd);
}
// If resource parallelism was requested, turn it on.
let parallel: number | undefined;
// If parallel was passed, validate it is an number
if (argv["parallel"]) {
parallel = parseInt(argv["parallel"], 10);
if (isNaN(parallel)) {
if (isNaN(parseInt(argv["parallel"], 10))) {
return printErrorUsageAndExit(
`error: --parallel flag must specify a number: ${argv["parallel"]} is not a number`);
}
}
// If there is a --dry-run directive, flip the switch. This controls whether we are planning vs. really doing it.
const dryRun: boolean = !!(argv["dry-run"]);
// If this is a typescript project, we'll want to load node-ts
const typeScript: boolean = process.env["PULUMI_NODEJS_TYPESCRIPT"] === "true";
// If there is a monitor argument, connect to it.
// Ensure a monitor address was passed
const monitorAddr = argv["monitor"];
if (!monitorAddr) {
return printErrorUsageAndExit(`error: --monitor=addr must be provided.`);
}
// If there is an engine argument, connect to it too.
const engineAddr: string | undefined = argv["engine"];
// Now configure the runtime and get it ready to run the program.
runtime.setOptions({
project: project,
stack: stack,
dryRun: dryRun,
parallel: parallel,
monitorAddr: monitorAddr,
engineAddr: engineAddr,
});
if (typeScript) {
tsnode.register({
typeCheck: true,
compilerOptions: {
target: "es6",
module: "commonjs",
moduleResolution: "node",
sourceMap: "true",
},
});
}
// Pluck out the program and arguments.
// Finally, ensure we have a program to run.
if (argv._.length === 0) {
return printErrorUsageAndExit("error: Missing program to execute");
}
let program: string = argv._[0];
if (program.indexOf("/") !== 0) {
// If this isn't an absolute path, make it relative to the working directory.
program = path.join(process.cwd(), program);
// Due to node module loading semantics, multiple copies of @pulumi/pulumi could be loaded at runtime. So we need
// to squirel these settings in the environment such that other copies which may be loaded later can recover them.
//
// Config is already an environment variaible set by the language plugin.
addToEnvIfDefined("PULUMI_NODEJS_PROJECT", argv["project"]);
addToEnvIfDefined("PULUMI_NODEJS_STACK", argv["stack"]);
addToEnvIfDefined("PULUMI_NODEJS_DRY_RUN", argv["dry-run"]);
addToEnvIfDefined("PULUMI_NODEJS_PARALLEL", argv["parallel"]);
addToEnvIfDefined("PULUMI_NODEJS_MONITOR", argv["monitor"]);
addToEnvIfDefined("PULUMI_NODEJS_ENGINE", argv["engineAddr"]);
require("./run").run(argv);
}
function addToEnvIfDefined(key: string, value: string | undefined) {
if (value) {
process.env[key] = value;
}
// Now fake out the process-wide argv, to make the program think it was run normally.
const programArgs: string[] = argv._.slice(1);
process.argv = [ process.argv[0], process.argv[1], ...programArgs ];
// Set up the process uncaught exception, unhandled rejection, and program exit handlers.
let uncaught: Error | undefined;
const uncaughtHandler = (err: Error) => {
// First, log the error.
if (RunError.isInstance(err)) {
// For errors that are subtypes of RunError, we will print the message without hitting the unhandled error
// logic, which will dump all sorts of verbose spew like the origin source and stack trace.
log.error(err.message);
}
else {
log.error(`Running program '${program}' failed with an unhandled exception:`);
log.error(util.format(err));
}
// Remember that we failed with an error. Don't quit just yet so we have a chance to drain the message loop.
uncaught = err;
};
process.on("uncaughtException", uncaughtHandler);
process.on("unhandledRejection", uncaughtHandler);
process.on("exit", (code: number) => {
runtime.disconnectSync();
// If we don't already have an exit code, and we had an unhandled error, exit with a non-success.
if (code === 0 && uncaught) {
process.exit(1);
}
});
// Construct a `Stack` resource to represent the outputs of the program.
runtime.runInPulumiStack(() => {
// We run the program inside this context so that it adopts all resources.
//
// IDEA: This will miss any resources created on other turns of the event loop. I think that's a fundamental
// problem with the current Component design though - not sure what else we could do here.
//
// Now go ahead and execute the code. The process will remain alive until the message loop empties.
log.debug(`Running program '${program}' in pwd '${process.cwd()}' w/ args: ${programArgs}`);
try {
return require(program);
} catch (e) {
// User JavaScript can throw anything, so if it's not an Error it's definitely
// not something we want to catch up here.
if (!(e instanceof Error)) {
throw e;
}
// Give a better error message, if we can.
const errorCode = (<any>e).code;
if (errorCode === "MODULE_NOT_FOUND") {
reportModuleLoadFailure(program, e);
}
throw e;
}
});
}
main(process.argv.slice(2));

211
sdk/nodejs/cmd/run/run.ts Normal file
View file

@ -0,0 +1,211 @@
// Copyright 2016-2018, 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.
// This is the entrypoint for running a Node.js program with minimal scaffolding.
import * as fs from "fs";
import * as minimist from "minimist";
import * as path from "path";
import * as tsnode from "ts-node";
import * as util from "util";
import { RunError } from "../../errors";
import * as log from "../../log";
import * as runtime from "../../runtime";
/**
* Attempts to provide a detailed error message for module load failure if the
* module that failed to load is the top-level module.
* @param program The name of the program given to `run`, i.e. the top level module
* @param error The error that occured. Must be a module load error.
*/
function reportModuleLoadFailure(program: string, error: Error): never {
// error is guaranteed to be a Node module load error. Node emits a very
// specific string in its error message for module load errors, which includes
// the module it was trying to load.
const errorRegex = /Cannot find module '(.*)'/;
// If there's no match, who knows what this exception is; it's not something
// we can provide an intelligent diagnostic for.
const moduleNameMatches = errorRegex.exec(error.message);
if (moduleNameMatches === null) {
throw error;
}
// Is the module that failed to load exactly the one that this script considered to
// be the top-level module for this program?
//
// We are only interested in producing good diagnostics for top-level module loads,
// since anything else are probably user code issues.
const moduleName = moduleNameMatches[1];
if (moduleName !== program) {
throw error;
}
console.error(`We failed to locate the entry point for your program: ${program}`);
// From here on out, we're going to try to inspect the program we're being asked to run
// a little to see what sort of details we can glean from it, in the hopes of producing
// a better error message.
//
// The first step of this is trying to slurp up a package.json for this program, if
// one exists.
const stat = fs.lstatSync(program);
let projectRoot: string;
if (stat.isDirectory()) {
projectRoot = program;
} else {
projectRoot = path.dirname(program);
}
let packageObject: Record<string, any>;
try {
const packageJson = path.join(projectRoot, "package.json");
packageObject = require(packageJson);
} catch {
// This is all best-effort so if we can't load the package.json file, that's
// fine.
return process.exit(1);
}
console.error("Here's what we think went wrong:");
// The objective here is to emit the best diagnostic we can, starting from the
// most specific to the least specific.
const deps = packageObject["dependencies"] || {};
const devDeps = packageObject["devDependencies"] || {};
const scripts = packageObject["scripts"] || {};
const mainProperty = packageObject["main"] || "index.js";
// Is there a build script associated with this program? It's a little confusing that the
// Pulumi CLI doesn't run build scripts before running the program so call that out
// explicitly.
if ("build" in scripts) {
const command = scripts["build"];
console.error(` * Your program looks like it has a build script associated with it ('${command}').\n`);
console.error("Pulumi does not run build scripts before running your program. " +
`Please run '${command}', 'yarn build', or 'npm run build' and try again.`);
return process.exit(1);
}
// Not all typescript programs have build scripts. If we think it's a typescript program,
// tell the user to run tsc.
if ("typescript" in deps || "typescript" in devDeps) {
console.error(" * Your program looks like a TypeScript program. Have you run 'tsc'?");
return process.exit(1);
}
// Not all projects are typescript. If there's a main property, check that the file exists.
if (mainProperty !== undefined && typeof mainProperty === "string") {
const mainFile = path.join(projectRoot, mainProperty);
if (!fs.existsSync(mainFile)) {
console.error(` * Your program's 'main' file (${mainFile}) does not exist.`);
return process.exit(1);
}
}
console.error(" * Yowzas, our sincere apologies, we haven't seen this before!");
console.error(` Here is the raw exception message we received: ${error.message}`);
return process.exit(1);
}
export function run(argv: minimist.ParsedArgs): void {
// If there is a --pwd directive, switch directories.
const pwd: string | undefined = argv["pwd"];
if (pwd) {
process.chdir(pwd);
}
// If this is a typescript project, we'll want to load node-ts.
const typeScript: boolean = process.env["PULUMI_NODEJS_TYPESCRIPT"] === "true";
if (typeScript) {
tsnode.register({
typeCheck: true,
compilerOptions: {
target: "es6",
module: "commonjs",
moduleResolution: "node",
sourceMap: "true",
},
});
}
let program: string = argv._[0];
if (program.indexOf("/") !== 0) {
// If this isn't an absolute path, make it relative to the working directory.
program = path.join(process.cwd(), program);
}
// Now fake out the process-wide argv, to make the program think it was run normally.
const programArgs: string[] = argv._.slice(1);
process.argv = [ process.argv[0], process.argv[1], ...programArgs ];
// Set up the process uncaught exception, unhandled rejection, and program exit handlers.
let uncaught: Error | undefined;
const uncaughtHandler = (err: Error) => {
// First, log the error.
if (RunError.isInstance(err)) {
// For errors that are subtypes of RunError, we will print the message without hitting the unhandled error
// logic, which will dump all sorts of verbose spew like the origin source and stack trace.
log.error(err.message);
}
else {
log.error(`Running program '${program}' failed with an unhandled exception:`);
log.error(util.format(err));
}
// Remember that we failed with an error. Don't quit just yet so we have a chance to drain the message loop.
uncaught = err;
};
process.on("uncaughtException", uncaughtHandler);
process.on("unhandledRejection", uncaughtHandler);
process.on("exit", (code: number) => {
runtime.disconnectSync();
// If we don't already have an exit code, and we had an unhandled error, exit with a non-success.
if (code === 0 && uncaught) {
process.exit(1);
}
});
// Construct a `Stack` resource to represent the outputs of the program.
runtime.runInPulumiStack(() => {
// We run the program inside this context so that it adopts all resources.
//
// IDEA: This will miss any resources created on other turns of the event loop. I think that's a fundamental
// problem with the current Component design though - not sure what else we could do here.
//
// Now go ahead and execute the code. The process will remain alive until the message loop empties.
log.debug(`Running program '${program}' in pwd '${process.cwd()}' w/ args: ${programArgs}`);
try {
return require(program);
} catch (e) {
// User JavaScript can throw anything, so if it's not an Error it's definitely
// not something we want to catch up here.
if (!(e instanceof Error)) {
throw e;
}
// Give a better error message, if we can.
const errorCode = (<any>e).code;
if (errorCode === "MODULE_NOT_FOUND") {
reportModuleLoadFailure(program, e);
}
throw e;
}
});
}

View file

@ -17,13 +17,12 @@
*/
const configEnvKey = "PULUMI_CONFIG";
const config: {[key: string]: string} = {};
const config: {[key: string]: string} = parseConfig();
/**
* allConfig returns a copy of the full config map.
*/
export function allConfig(): {[key: string]: string} {
ensureConfig();
return Object.assign({}, config);
}
@ -31,7 +30,6 @@ export function allConfig(): {[key: string]: string} {
* setConfig sets a configuration variable.
*/
export function setConfig(k: string, v: string): void {
ensureConfig();
config[cleanKey(k)] = v;
}
@ -39,29 +37,20 @@ export function setConfig(k: string, v: string): void {
* getConfig returns a configuration variable's value or undefined if it is unset.
*/
export function getConfig(k: string): string | undefined {
ensureConfig();
return config[k];
}
/**
* loaded is set to true if and when we've attempted to load config from the environment.
*/
let loaded: boolean = false;
/**
* ensureConfig populates the runtime.config object based on configuration set in the environment.
*/
export function ensureConfig() {
if (!loaded) {
const envConfig = process.env.PULUMI_CONFIG;
if (envConfig) {
const envObject: {[key: string]: string} = JSON.parse(envConfig);
for (const k of Object.keys(envObject)) {
config[cleanKey(k)] = envObject[k];
}
function parseConfig() {
const parsedConfig: {[key: string]: string} = {};
const envConfig = process.env[configEnvKey];
if (envConfig) {
const envObject: {[key: string]: string} = JSON.parse(envConfig);
for (const k of Object.keys(envObject)) {
parsedConfig[cleanKey(k)] = envObject[k];
}
loaded = true;
}
return parsedConfig;
}
/**

View file

@ -12,10 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import * as minimist from "minimist";
import { RunError } from "../errors";
import { Resource } from "../resource";
import { ensureConfig } from "./config";
import { debuggablePromise } from "./debuggable";
const grpc = require("grpc");
@ -42,40 +40,27 @@ export interface Options {
/**
* _options are the current deployment options being used for this entire session.
*/
let _options: Options | undefined;
/**
* options fetches the current configured options and, if required, lazily initializes them.
*/
function options(): Options {
if (!_options) {
// See if the options are available in memory. This would happen if we load the pulumi SDK multiple
// times into the same heap. In this case, the entry point would have configured one copy of the library,
// which has an independent set of statics. But it left behind the configured state in environment variables.
_options = loadOptions();
}
return _options;
}
const options = loadOptions();
/**
* Returns true if we're currently performing a dry-run, or false if this is a true update.
*/
export function isDryRun(): boolean {
return options().dryRun === true;
return options.dryRun === true;
}
/**
* Get the project being run by the current update.
*/
export function getProject(): string | undefined {
return options().project;
return options.project;
}
/**
* Get the stack being targeted by the current update.
*/
export function getStack(): string | undefined {
return options().stack;
return options.stack;
}
/**
@ -87,7 +72,7 @@ let monitor: any | undefined;
* hasMonitor returns true if we are currently connected to a resource monitoring service.
*/
export function hasMonitor(): boolean {
return !!monitor && !!options().monitorAddr;
return !!monitor && !!options.monitorAddr;
}
/**
@ -95,7 +80,7 @@ export function hasMonitor(): boolean {
*/
export function getMonitor(): Object {
if (!monitor) {
const addr = options().monitorAddr;
const addr = options.monitorAddr;
if (addr) {
// Lazily initialize the RPC connection to the monitor.
monitor = new resrpc.ResourceMonitorClient(addr, grpc.credentials.createInsecure());
@ -118,7 +103,7 @@ let engine: any | undefined;
*/
export function getEngine(): Object | undefined {
if (!engine) {
const addr = options().engineAddr;
const addr = options.engineAddr;
if (addr) {
// Lazily initialize the RPC connection to the engine.
engine = new engrpc.EngineClient(addr, grpc.credentials.createInsecure());
@ -131,51 +116,15 @@ export function getEngine(): Object | undefined {
* serialize returns true if resource operations should be serialized.
*/
export function serialize(): boolean {
const p = options().parallel;
const p = options.parallel;
return !p || p <= 1;
}
/**
* setOptions initializes the current runtime with information about whether we are performing a "dry
* run" (preview), versus a real deployment, RPC addresses, and so on. It may only be called once.
*/
export function setOptions(opts: Options): void {
if (_options) {
throw new Error("Cannot configure runtime settings more than once");
}
// Set environment variables so other copies of the library can do the right thing.
if (opts.project !== undefined) {
process.env["PULUMI_NODEJS_PROJECT"] = opts.project;
}
if (opts.stack !== undefined) {
process.env["PULUMI_NODEJS_STACK"] = opts.stack;
}
if (opts.dryRun !== undefined) {
process.env["PULUMI_NODEJS_DRY_RUN"] = opts.dryRun.toString();
}
if (opts.parallel !== undefined) {
process.env["PULUMI_NODEJS_PARALLEL"] = opts.parallel.toString();
}
if (opts.monitorAddr !== undefined) {
process.env["PULUMI_NODEJS_MONITOR"] = opts.monitorAddr;
}
if (opts.engineAddr !== undefined) {
process.env["PULUMI_NODEJS_ENGINE"] = opts.engineAddr;
}
// Now, save the in-memory static state. All RPC connections will be created lazily as required.
_options = opts;
}
/**
* loadOptions recovers previously configured options in the case that a copy of the runtime SDK library
* is loaded without going through the entry point shim, as happens when multiple copies are loaded.
* loadOptions recovers the options from the environment, which is set before we begin executing. This ensures
* that even when multiple copies of this module are loaded, they all get the same values.
*/
function loadOptions(): Options {
// Load the config from the environment.
ensureConfig();
// The only option that needs parsing is the parallelism flag. Ignore any failures.
let parallel: number | undefined;
const parallelOpt = process.env["PULUMI_NODEJS_PARALLEL"];

View file

@ -4824,7 +4824,7 @@ Module './bin/index.js' is a 'deployment only' module. In general these cannot b
var __testConfig_proto = {};
__f1.prototype = __testConfig_proto;
Object.defineProperty(__testConfig_proto, "constructor", { configurable: true, writable: true, value: __f1 });
var __config = {["test:TestingKey1"]: "TestingValue1", ["test:TestingKey2"]: "TestingValue2"(...)
var __config = {["test:TestingKey1"]: "TestingValue1", ["test:TestingKey2"]: "TestingValue2", ["pkg:a"]: "foo", ["pkg:bar"]: "b", ["pkg:baz"]: "baz", ["otherpkg:a"]: "babble", ["otherpkg:nothere"]: "bazzle", ["pkg:boolf"]: "false", ["pkg:boolt"]: "true", ["pkg:num"]: "42.333", ["pkg:array"]: "[ 0, false, 2, \\"foo\\" ]", ["pkg:struct"]: "{ \\"foo\\": \\"bar\\", \\"mim\\": [] }"};
var __runtime_1 = {getConfig: __getConfig};
Object.defineProperty(__testConfig_proto, "get", { configurable: true, writable: true, value: __f2 });
__f5.isInstance = __f6;
@ -4841,39 +4841,17 @@ Object.defineProperty(__testConfig_proto, "requireObject", { configurable: true,
Object.defineProperty(__testConfig_proto, "fullKey", { configurable: true, writable: true, value: __f14 });
var __testConfig = Object.create(__testConfig_proto);
__testConfig.name = "test";
(...)
function __cleanKey() {
function __f1() {
return (function() {
with({ cleanKey: __cleanKey }) {
with({ }) {
return function /*cleanKey*/(key) {
const idx = key.indexOf(":");
if (idx > 0 && key.startsWith("config:", idx + 1)) {
return key.substring(0, idx) + ":" + key.substring(idx + 1 + "config:".length);
}
return key;
};
}
}).apply(undefined, undefined).apply(this, arguments);
}
function __ensureConfig() {
return (function() {
with({ loaded: true, config: __config, cleanKey: __cleanKey, ensureConfig: __ensureConfig }) {
return function /*ensureConfig*/() {
if (!loaded) {
const envConfig = process.env.PULUMI_CONFIG;
if (envConfig) {
const envObject = JSON.parse(envConfig);
for (const k of Object.keys(envObject)) {
config[cleanKey(k)] = envObject[k];
}
return function /*constructor*/(name) {
if (name.endsWith(":config")) {
name = name.replace(/:config$/, "");
}
loaded = true;
}
};
this.name = name;
};
}
}).apply(undefined, undefined).apply(this, arguments);
@ -4881,10 +4859,9 @@ return function /*ensureConfig*/() {
function __getConfig() {
return (function() {
with({ ensureConfig: __ensureConfig, config: __config, getConfig: __getConfig }) {
with({ config: __config, getConfig: __getConfig }) {
return function /*getConfig*/(k) {
ensureConfig();
return config[k];
};
@ -4927,7 +4904,7 @@ return function () { const v = testConfig.get("TestingKey1"); console.log(v); };
var __f1_prototype = {};
Object.defineProperty(__f1_prototype, "constructor", { configurable: true, writable: true, value: __f1 });
var __config = {["test:TestingKey1"]: "TestingValue1", ["test:TestingKey2"]: "TestingValue2"(...)
var __config = {["test:TestingKey1"]: "TestingValue1", ["test:TestingKey2"]: "TestingValue2", ["pkg:a"]: "foo", ["pkg:bar"]: "b", ["pkg:baz"]: "baz", ["otherpkg:a"]: "babble", ["otherpkg:nothere"]: "bazzle", ["pkg:boolf"]: "false", ["pkg:boolt"]: "true", ["pkg:num"]: "42.333", ["pkg:array"]: "[ 0, false, 2, \\"foo\\" ]", ["pkg:struct"]: "{ \\"foo\\": \\"bar\\", \\"mim\\": [] }"};
var __runtime_1 = {getConfig: __getConfig};
Object.defineProperty(__f1_prototype, "get", { configurable: true, writable: true, value: __f2 });
__f5.isInstance = __f6;
@ -4944,39 +4921,17 @@ Object.defineProperty(__f1_prototype, "requireObject", { configurable: true, wri
Object.defineProperty(__f1_prototype, "fullKey", { configurable: true, writable: true, value: __f14 });
__f1.prototype = __f1_prototype;
var __pulumi = {Config: __f1};
(...)
function __cleanKey() {
function __f1() {
return (function() {
with({ cleanKey: __cleanKey }) {
with({ }) {
return function /*cleanKey*/(key) {
const idx = key.indexOf(":");
if (idx > 0 && key.startsWith("config:", idx + 1)) {
return key.substring(0, idx) + ":" + key.substring(idx + 1 + "config:".length);
}
return key;
};
}
}).apply(undefined, undefined).apply(this, arguments);
}
function __ensureConfig() {
return (function() {
with({ loaded: true, config: __config, cleanKey: __cleanKey, ensureConfig: __ensureConfig }) {
return function /*ensureConfig*/() {
if (!loaded) {
const envConfig = process.env.PULUMI_CONFIG;
if (envConfig) {
const envObject = JSON.parse(envConfig);
for (const k of Object.keys(envObject)) {
config[cleanKey(k)] = envObject[k];
}
return function /*constructor*/(name) {
if (name.endsWith(":config")) {
name = name.replace(/:config$/, "");
}
loaded = true;
}
};
this.name = name;
};
}
}).apply(undefined, undefined).apply(this, arguments);
@ -4984,10 +4939,9 @@ return function /*ensureConfig*/() {
function __getConfig() {
return (function() {
with({ ensureConfig: __ensureConfig, config: __config, getConfig: __getConfig }) {
with({ config: __config, getConfig: __getConfig }) {
return function /*getConfig*/(k) {
ensureConfig();
return config[k];
};

View file

@ -47,6 +47,7 @@
"cmd/dynamic-provider/index.ts",
"cmd/run/index.ts",
"cmd/run/run.ts",
"tests/config.spec.ts",
"tests/init.spec.ts",