pulumi/sdk/nodejs/config.ts
Joe Duffy 444ebdd1b5
Improve failure messages (#932)
This improves the failure messages in two circumstances:

1) If the resource monitor RPC connection is missing.  This can happen
   two ways: either you run a Pulumi program using vanilla Node.js, instead
   of the CLI, or you've accidentally loaded the Pulumi SDK more than once.

2) Failure to load the custom Pulumi SDK Node.js extension.  This is a new
   addition and would happen if you tried running a Pulumi program using a
   vanilla Node.js, rather than using the Pulumi CLI.
2018-02-14 09:55:02 -08:00

172 lines
5.7 KiB
TypeScript

// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
import { RunError } from "./errors";
import * as runtime from "./runtime";
/**
* Config is a bag of related configuration state. Each bag contains any number of configuration variables, indexed by
* simple keys, and each has a name that uniquely identifies it; two bags with different names do not share values for
* variables that otherwise share the same key. For example, a bag whose name is `pulumi:foo`, with keys `a`, `b`,
* and `c`, is entirely separate from a bag whose name is `pulumi:bar` with the same simple key names. Each key has a
* fully qualified names, such as `pulumi:foo:a`, ..., and `pulumi:bar:a`, respectively.
*/
export class Config {
/**
* name is the configuration bag's logical name and uniquely identifies it.
*/
public readonly name: string;
constructor(name: string) {
this.name = name;
}
/**
* get loads an optional configuration value by its key, or undefined if it doesn't exist.
*
* @param key The key to lookup.
*/
public get(key: string): string | undefined {
return runtime.getConfig(this.fullKey(key));
}
/**
* getBoolean loads an optional configuration value, as a boolean, by its key, or undefined if it doesn't exist.
* If the configuration value isn't a legal boolean, this function will throw an error.
*
* @param key The key to lookup.
*/
public getBoolean(key: string): boolean | undefined {
const v: string | undefined = this.get(key);
if (v === undefined) {
return undefined;
} else if (v === "true") {
return true;
} else if (v === "false") {
return false;
}
throw new ConfigTypeError(this.fullKey(key), v, "boolean");
}
/**
* getNumber loads an optional configuration value, as a number, by its key, or undefined if it doesn't exist.
* If the configuration value isn't a legal number, this function will throw an error.
*
* @param key The key to lookup.
*/
public getNumber(key: string): number | undefined {
const v: string | undefined = this.get(key);
if (v === undefined) {
return undefined;
}
const f: number = parseFloat(v);
if (isNaN(f)) {
throw new ConfigTypeError(this.fullKey(key), v, "number");
}
return f;
}
/**
* getObject loads an optional configuration value, as an object, by its key, or undefined if it doesn't exist.
* This routine simply JSON parses and doesn't validate the shape of the contents.
*
* @param key The key to lookup.
*/
public getObject<T>(key: string): T | undefined {
const v: string | undefined = this.get(key);
if (v === undefined) {
return undefined;
}
try {
return <T>JSON.parse(v);
}
catch (err) {
throw new ConfigTypeError(this.fullKey(key), v, "JSON object");
}
}
/**
* require loads a configuration value by its given key. If it doesn't exist, an error is thrown.
*
* @param key The key to lookup.
*/
public require(key: string): string {
const v: string | undefined = this.get(key);
if (v === undefined) {
throw new ConfigMissingError(this.fullKey(key));
}
return v;
}
/**
* requireBoolean loads a configuration value, as a boolean, by its given key. If it doesn't exist, or the
* configuration value is not a legal boolean, an error is thrown.
*
* @param key The key to lookup.
*/
public requireBoolean(key: string): boolean {
const v: boolean | undefined = this.getBoolean(key);
if (v === undefined) {
throw new ConfigMissingError(this.fullKey(key));
}
return v;
}
/**
* requireNumber loads a configuration value, as a number, by its given key. If it doesn't exist, or the
* configuration value is not a legal number, an error is thrown.
*
* @param key The key to lookup.
*/
public requireNumber(key: string): number {
const v: number | undefined = this.getNumber(key);
if (v === undefined) {
throw new ConfigMissingError(this.fullKey(key));
}
return v;
}
/**
* requireObject loads a configuration value, as a number, by its given key. If it doesn't exist, or the
* configuration value is not a legal number, an error is thrown.
*
* @param key The key to lookup.
*/
public requireObject<T>(key: string): T {
const v: T | undefined = this.getObject<T>(key);
if (v === undefined) {
throw new ConfigMissingError(this.fullKey(key));
}
return v;
}
/**
* fullKey turns a simple configuration key into a fully resolved one, by prepending the bag's name.
*
* @param key The key to lookup.
*/
private fullKey(key: string): string {
return `${this.name}:${key}`;
}
}
/**
* ConfigTypeError is used when a configuration value is of the wrong type.
*/
class ConfigTypeError extends RunError {
constructor(key: string, v: any, expectedType: string) {
super(`Configuration '${key}' value '${v}' is not a valid ${expectedType}`);
}
}
/**
* ConfigMissingError is used when a configuration value is completely missing.
*/
class ConfigMissingError extends RunError {
constructor(public key: string) {
super(
`Missing required configuration variable '${key}'\n` +
`\tplease set a value using the command \`pulumi config set ${key} <value>\``,
);
}
}