fix nodejs resource functions to properly propagate errors (#6644)
This commit is contained in:
parent
1140e9f2bf
commit
d098f9181b
|
@ -18,6 +18,9 @@
|
|||
|
||||
### Bug Fixes
|
||||
|
||||
- [sdk/nodejs] Fix error propagation in registerResource and other resource methods.
|
||||
[#6644](https://github.com/pulumi/pulumi/pull/6644)
|
||||
|
||||
- [automation/python] Fix passing of additional environment variables.
|
||||
[#6639](https://github.com/pulumi/pulumi/pull/6639)
|
||||
|
||||
|
|
|
@ -416,21 +416,34 @@ async function prepareResource(label: string, res: Resource, custom: boolean,
|
|||
let resolveURN: (urn: URN, err?: Error) => void;
|
||||
{
|
||||
let resolveValue: (urn: URN) => void;
|
||||
let rejectValue: (err: Error) => void;
|
||||
let resolveIsKnown: (isKnown: boolean) => void;
|
||||
let rejectIsKnown: (err: Error) => void;
|
||||
(res as any).urn = new Output(
|
||||
res,
|
||||
debuggablePromise(
|
||||
new Promise<URN>(resolve => resolveValue = resolve),
|
||||
new Promise<URN>((resolve, reject) => {
|
||||
resolveValue = resolve;
|
||||
rejectValue = reject;
|
||||
}),
|
||||
`resolveURN(${label})`),
|
||||
debuggablePromise(
|
||||
new Promise<boolean>(resolve => resolveIsKnown = resolve),
|
||||
new Promise<boolean>((resolve, reject) => {
|
||||
resolveIsKnown = resolve;
|
||||
rejectIsKnown = reject;
|
||||
}),
|
||||
`resolveURNIsKnown(${label})`),
|
||||
/*isSecret:*/ Promise.resolve(false),
|
||||
Promise.resolve(res));
|
||||
|
||||
resolveURN = (v, err) => {
|
||||
resolveValue(v);
|
||||
resolveIsKnown(err === undefined);
|
||||
if (!!err) {
|
||||
rejectValue(err);
|
||||
rejectIsKnown(err);
|
||||
} else {
|
||||
resolveValue(v);
|
||||
resolveIsKnown(true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -438,19 +451,32 @@ async function prepareResource(label: string, res: Resource, custom: boolean,
|
|||
let resolveID: ((v: any, performApply: boolean, err?: Error) => void) | undefined;
|
||||
if (custom) {
|
||||
let resolveValue: (v: ID) => void;
|
||||
let rejectValue: (err: Error) => void;
|
||||
let resolveIsKnown: (v: boolean) => void;
|
||||
let rejectIsKnown: (err: Error) => void;
|
||||
|
||||
(res as any).id = new Output(
|
||||
res,
|
||||
debuggablePromise(new Promise<ID>(resolve => resolveValue = resolve),
|
||||
debuggablePromise(new Promise<ID>((resolve, reject) => {
|
||||
resolveValue = resolve;
|
||||
rejectValue = reject;
|
||||
}),
|
||||
`resolveID(${label})`),
|
||||
debuggablePromise(new Promise<boolean>(
|
||||
resolve => resolveIsKnown = resolve), `resolveIDIsKnown(${label})`),
|
||||
debuggablePromise(new Promise<boolean>((resolve, reject) => {
|
||||
resolveIsKnown = resolve;
|
||||
rejectIsKnown = reject;
|
||||
}), `resolveIDIsKnown(${label})`),
|
||||
Promise.resolve(false),
|
||||
Promise.resolve(res));
|
||||
|
||||
resolveID = (v, isKnown, err) => {
|
||||
resolveValue(v);
|
||||
resolveIsKnown(err ? false : isKnown);
|
||||
if (!!err) {
|
||||
rejectValue(err);
|
||||
rejectIsKnown(err);
|
||||
} else {
|
||||
resolveValue(v);
|
||||
resolveIsKnown(isKnown);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -52,31 +52,54 @@ export function transferProperties(onto: Resource, label: string, props: Inputs)
|
|||
}
|
||||
|
||||
let resolveValue: (v: any) => void;
|
||||
let rejectValue: (err: Error) => void;
|
||||
let resolveIsKnown: (v: boolean) => void;
|
||||
let rejectIsKnown: (err: Error) => void;
|
||||
let resolveIsSecret: (v: boolean) => void;
|
||||
let rejectIsSecret: (err: Error) => void;
|
||||
let resolveDeps: (v: Resource[]) => void;
|
||||
let rejectDeps: (err: Error) => void;
|
||||
|
||||
resolvers[k] = (v: any, isKnown: boolean, isSecret: boolean, deps: Resource[] = [], err?: Error) => {
|
||||
resolveValue(v);
|
||||
resolveIsKnown(err ? false : isKnown);
|
||||
resolveIsSecret(isSecret);
|
||||
resolveDeps(deps);
|
||||
if (!!err) {
|
||||
rejectValue(err);
|
||||
rejectIsKnown(err);
|
||||
rejectIsSecret(err);
|
||||
rejectDeps(err);
|
||||
} else {
|
||||
resolveValue(v);
|
||||
resolveIsKnown(isKnown);
|
||||
resolveIsSecret(isSecret);
|
||||
resolveDeps(deps);
|
||||
}
|
||||
};
|
||||
|
||||
const propString = Output.isInstance(props[k]) ? "Output<T>" : `${props[k]}`;
|
||||
(<any>onto)[k] = new Output(
|
||||
onto,
|
||||
debuggablePromise(
|
||||
new Promise<any>(resolve => resolveValue = resolve),
|
||||
new Promise<any>((resolve, reject) => {
|
||||
resolveValue = resolve;
|
||||
rejectValue = reject;
|
||||
}),
|
||||
`transferProperty(${label}, ${k}, ${propString})`),
|
||||
debuggablePromise(
|
||||
new Promise<boolean>(resolve => resolveIsKnown = resolve),
|
||||
new Promise<boolean>((resolve, reject) => {
|
||||
resolveIsKnown = resolve;
|
||||
rejectIsKnown = reject;
|
||||
}),
|
||||
`transferIsStable(${label}, ${k}, ${propString})`),
|
||||
debuggablePromise(
|
||||
new Promise<boolean>(resolve => resolveIsSecret = resolve),
|
||||
new Promise<boolean>((resolve, reject) => {
|
||||
resolveIsSecret = resolve;
|
||||
rejectIsSecret = reject;
|
||||
}),
|
||||
`transferIsSecret(${label}, ${k}, ${propString})`),
|
||||
debuggablePromise(
|
||||
new Promise<Resource[]>(resolve => resolveDeps = resolve),
|
||||
new Promise<Resource[]>((resolve, reject) => {
|
||||
resolveDeps = resolve;
|
||||
rejectDeps = reject;
|
||||
}),
|
||||
`transferDeps(${label}, ${k}, ${propString})`));
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,12 @@ class TestCustomResource extends CustomResource {
|
|||
}
|
||||
}
|
||||
|
||||
class TestErrorResource extends CustomResource {
|
||||
constructor(name: string) {
|
||||
super("error", name, {});
|
||||
}
|
||||
}
|
||||
|
||||
class TestResourceModule implements runtime.ResourceModule {
|
||||
construct(name: string, type: string, urn: string): Resource {
|
||||
switch (type) {
|
||||
|
@ -60,6 +66,8 @@ class TestMocks implements runtime.Mocks {
|
|||
id: runtime.isDryRun() ? undefined : "test-id",
|
||||
state: {},
|
||||
};
|
||||
case "error":
|
||||
throw new Error("this is an intentional error");
|
||||
default:
|
||||
throw new Error(`unknown resource type ${type}`);
|
||||
}
|
||||
|
@ -349,4 +357,20 @@ describe("runtime", () => {
|
|||
assert.deepEqual(deserialized["unregistered"], unregisteredID);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("resource error handling", () => {
|
||||
it("registerResource errors propagate appropriately", asyncTest(async () => {
|
||||
runtime.setMocks(new TestMocks());
|
||||
|
||||
await assert.rejects(async () => {
|
||||
const errResource = new TestErrorResource("test");
|
||||
const customURN = await errResource.urn.promise();
|
||||
const customID = await errResource.id.promise();
|
||||
}, (err: Error) => {
|
||||
const containsMessage = err.stack!.indexOf("this is an intentional error") >= 0;
|
||||
const containsRegisterResource = err.stack!.indexOf("registerResource") >= 0;
|
||||
return containsMessage && containsRegisterResource;
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue