Make it possible to get a StackReference output promptly (#2824)

This commit is contained in:
CyrusNajmabadi 2019-06-17 12:25:56 -07:00 committed by GitHub
parent 0b4d94a239
commit 11a19a4990
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 119 additions and 1 deletions

View file

@ -2,6 +2,12 @@
- Allow setting backend URL explicitly in `Pulumi.yaml` file
- `StackReference` now has a `.getOutputSync` function to retrieve exported values from an existing
stack synchronously. This can be valuable when creating another stack that wants to base
flow-control off of the values of an existing stack (i.e. importing the information about all AZs
and basing logic off of that in a new stack). Note: this only works for importing values from
Stacks that have not exported `secrets`.
## 0.17.17 (Released June 12, 2019)
### Improvements

View file

@ -21,7 +21,8 @@
"source-map-support": "^0.4.16",
"ts-node": "^7.0.0",
"typescript": "^3.0.0",
"upath": "^1.1.0"
"upath": "^1.1.0",
"deasync": "^0.1.15"
},
"devDependencies": {
"@types/minimist": "^1.2.0",
@ -30,6 +31,7 @@
"@types/normalize-package-data": "^2.4.0",
"@types/read-package-tree": "^5.2.0",
"@types/semver": "^5.5.0",
"@types/deasync": "^0.1.0",
"istanbul": "^0.4.5",
"mocha": "^3.5.0",
"node-gyp": "^3.6.2",

View file

@ -14,6 +14,7 @@
import { all, Input, Output, output } from "./output";
import { CustomResource, CustomResourceOptions } from "./resource";
import { promiseResult } from "./utils";
/**
* Manages a reference to a Pulumi stack. The referenced stack's outputs are available via the
@ -56,6 +57,25 @@ export class StackReference extends CustomResource {
public getOutput(name: Input<string>): Output<any> {
return all([output(name), this.outputs]).apply(([n, os]) => os[n]);
}
/**
* Fetches the value promptly of the named stack output. May return undefined if the value is
* not known for some reason.
*
* This operation is not supported (and will throw) if any exported values of the StackReference
* are secrets.
*
* @param name The name of the stack output to fetch.
*/
public getOutputSync(name: string): any {
const out = this.getOutput(name);
const isSecret = promiseResult(out.isSecret);
if (isSecret) {
throw new Error("Cannot call [getOutputSync] if the referenced stack has secret outputs. Use [getOutput] instead.");
}
return promiseResult(out.promise());
}
}
/**

View file

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import * as deasync from "deasync";
/**
* Common code for doing RTTI typechecks. RTTI is done by having a boolean property on an object
* with a special name (like "__resource" or "__asset"). This function checks that the object
@ -39,3 +41,42 @@ export function hasTrueBooleanMember(obj: any, memberName: string | number | sym
return val === true;
}
/**
* Synchronously blocks until the result of this promise is computed. If the promise is rejected,
* this will throw the error the promise was rejected with. If this promise does not complete this
* will block indefinitely.
*
* Be very careful with this function. Only wait on a promise if you are certain it is safe to do
* so.
*
* @internal
*/
export function promiseResult<T>(promise: Promise<T>): T {
enum State {
running,
finishedSuccessfully,
finishedWithError,
}
let result: T;
let error = undefined;
let state = <State>State.running;
promise.then(
val => {
result = val;
state = State.finishedSuccessfully;
},
err => {
error = err;
state = State.finishedWithError;
});
deasync.loopWhile(() => state === State.running);
if (state === State.finishedWithError) {
throw error;
}
return result!;
}

View file

@ -891,6 +891,16 @@ func TestStackReferenceNodeJS(t *testing.T) {
Config: map[string]string{
"org": os.Getenv("PULUMI_TEST_OWNER"),
},
EditDirs: []integration.EditDir{
{
Dir: "step1",
Additive: true,
},
{
Dir: "step2",
Additive: true,
},
},
}
integration.ProgramTest(t, opts)
}

View file

@ -6,3 +6,5 @@ let config = new pulumi.Config();
let org = config.require("org");
let slug = `${org}/${pulumi.getProject()}/${pulumi.getStack()}`;
let a = new pulumi.StackReference(slug);
export const val = ["a", "b"];

View file

@ -0,0 +1,15 @@
// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
import * as pulumi from "@pulumi/pulumi";
let config = new pulumi.Config();
let org = config.require("org");
let slug = `${org}/${pulumi.getProject()}/${pulumi.getStack()}`;
let a = new pulumi.StackReference(slug);
const oldVal: string[] = a.getOutputSync("val");
if (oldVal.length !== 2 || oldVal[0] !== "a" || oldVal[1] !== "b") {
throw new Error("Invalid result");
}
export const val2 = pulumi.secret(["a", "b"]);

View file

@ -0,0 +1,22 @@
// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
import * as pulumi from "@pulumi/pulumi";
let config = new pulumi.Config();
let org = config.require("org");
let slug = `${org}/${pulumi.getProject()}/${pulumi.getStack()}`;
let a = new pulumi.StackReference(slug);
let gotError = false;
try
{
a.getOutputSync("val2");
}
catch (err)
{
gotError = true;
}
if (!gotError) {
throw new Error("Expected to get error trying to read secret from stack reference.");
}