Update @pulumi/pulumi
to version 0.17.0 (#2510)
This update includes several changes to core `@pulumi/pulumi` constructs that will not play nicely in side-by-side applications that pull in prior versions of this package. As such, we are rev'ing the minor version of the package from 0.16 to 0.17. Recent version of `pulumi` will now detect, and warn, if different versions of `@pulumi/pulumi` are loaded into the same application. If you encounter this warning, it is recommended you move to versions of the `@pulumi/...` packages that are compatible. i.e. keep everything on 0.16.x until you are ready to move everything to 0.17.x. ### Improvements - `Output<T>` now 'lifts' property members from the value it wraps, simplifying common coding patterns. Note: this wrapping only happens for POJO values, not `Output<Resource>`s. - Depending on a **Component** Resource will now depend on all other Resources parented by that Resource. This will help out the programming model for Component Resources as your consumers can just depend on a Component and have that automatically depend on all the child Resources created by that Component. Note: this does not apply to a **Custom** resource. Depending on a CustomResource will still only wait on that single resource being created, not any other Resources that consider that CustomResource to be a parent.
This commit is contained in:
parent
905e7353e4
commit
7f5e089f04
22
CHANGELOG.md
22
CHANGELOG.md
|
@ -1,7 +1,27 @@
|
|||
## 0.16.19 (Unreleased)
|
||||
## 0.17.1 (unreleased)
|
||||
|
||||
### Improvements
|
||||
|
||||
## 0.17.0 (Released March 5, 2019)
|
||||
|
||||
This update includes several changes to core `@pulumi/pulumi` constructs that will not play nicely
|
||||
in side-by-side applications that pull in prior versions of this package. As such, we are rev'ing
|
||||
the minor version of the package from 0.16 to 0.17. Recent version of `pulumi` will now detect,
|
||||
and warn, if different versions of `@pulumi/pulumi` are loaded into the same application. If you
|
||||
encounter this warning, it is recommended you move to versions of the `@pulumi/...` packages that
|
||||
are compatible. i.e. keep everything on 0.16.x until you are ready to move everything to 0.17.x.
|
||||
|
||||
### Improvements
|
||||
|
||||
- `Output<T>` now 'lifts' property members from the value it wraps, simplifying common coding patterns.
|
||||
|
||||
- Depending on a **Component** Resource will now depend on all other Resources parented by that
|
||||
Resource. This will help out the programming model for Component Resources as your consumers can
|
||||
just depend on a Component and have that automatically depend on all the child Resources created
|
||||
by that Component. Note: this does not apply to a **Custom** resource. Depending on a
|
||||
CustomResource will still only wait on that single resource being created, not any other Resources
|
||||
that consider that CustomResource to be a parent.
|
||||
|
||||
## 0.16.18 (Released March 1, 2019)
|
||||
|
||||
- Fix an issue where the Pulumi CLI would load the newest plugin for a resource provider instead of the version that was
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import * as log from "./log";
|
||||
import { Resource } from "./resource";
|
||||
import * as runtime from "./runtime";
|
||||
import * as utils from "./utils";
|
||||
|
@ -23,7 +24,7 @@ import * as utils from "./utils";
|
|||
* value as well as the Resource the value came from. This allows for a precise 'Resource
|
||||
* dependency graph' to be created, which properly tracks the relationship between resources.
|
||||
*/
|
||||
export class Output<T> {
|
||||
class OutputImpl<T> implements OutputInstance<T> {
|
||||
/**
|
||||
* A private field to help with RTTI that works in SxS scenarios.
|
||||
*
|
||||
|
@ -56,43 +57,7 @@ export class Output<T> {
|
|||
*/
|
||||
/* @internal */ public readonly resources: () => Set<Resource>;
|
||||
|
||||
/**
|
||||
* Transforms the data of the output with the provided func. The result remains a
|
||||
* Output so that dependent resources can be properly tracked.
|
||||
*
|
||||
* 'func' is not allowed to make resources.
|
||||
*
|
||||
* 'func' can return other Outputs. This can be handy if you have a Output<SomeVal>
|
||||
* and you want to get a transitive dependency of it. i.e.
|
||||
*
|
||||
* ```ts
|
||||
* var d1: Output<SomeVal>;
|
||||
* var d2 = d1.apply(v => v.x.y.OtherOutput); // getting an output off of 'v'.
|
||||
* ```
|
||||
*
|
||||
* In this example, taking a dependency on d2 means a resource will depend on all the resources
|
||||
* of d1. It will *not* depend on the resources of v.x.y.OtherDep.
|
||||
*
|
||||
* Importantly, the Resources that d2 feels like it will depend on are the same resources as d1.
|
||||
* If you need have multiple Outputs and a single Output is needed that combines both
|
||||
* set of resources, then 'pulumi.all' should be used instead.
|
||||
*
|
||||
* This function will only be called execution of a 'pulumi update' request. It will not run
|
||||
* during 'pulumi preview' (as the values of resources are of course not known then). It is not
|
||||
* available for functions that end up executing in the cloud during runtime. To get the value
|
||||
* of the Output during cloud runtime execution, use `get()`.
|
||||
*/
|
||||
public readonly apply: <U>(func: (t: T) => Input<U>) => Output<U>;
|
||||
|
||||
/**
|
||||
* Retrieves the underlying value of this dependency.
|
||||
*
|
||||
* This function is only callable in code that runs in the cloud post-deployment. At this
|
||||
* point all Output values will be known and can be safely retrieved. During pulumi deployment
|
||||
* or preview execution this must not be called (and will throw). This is because doing so
|
||||
* would allow Output values to flow into Resources while losing the data that would allow
|
||||
* the dependency graph to be changed.
|
||||
*/
|
||||
public readonly get: () => T;
|
||||
|
||||
/**
|
||||
|
@ -251,6 +216,70 @@ This function may throw in a future version of @pulumi/pulumi.`;
|
|||
throw new Error(`Cannot call '.get' during update or preview.
|
||||
To manipulate the value of this Output, use '.apply' instead.`);
|
||||
};
|
||||
|
||||
return new Proxy(this, {
|
||||
get: (obj, prop: keyof T) => {
|
||||
// Recreate the prototype walk to ensure we find any actual members defined directly
|
||||
// on `Output<T>`.
|
||||
for (let o = obj; o; o = Object.getPrototypeOf(o)) {
|
||||
if (o.hasOwnProperty(prop)) {
|
||||
return (<any>o)[prop];
|
||||
}
|
||||
}
|
||||
|
||||
// Always explicitly fail on a member called 'then'. It is used by other systems to
|
||||
// determine if this is a Promise, and we do not want to indicate that that's what
|
||||
// we are.
|
||||
if (prop === "then") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Do not lift members that start with __. Technically, if all libraries were
|
||||
// using this version of pulumi/pulumi we would not need this. However, this is
|
||||
// so that downstream consumers can use this version of pulumi/pulumi while also
|
||||
// passing these new Outputs to older versions of pulumi/pulumi. The reason this
|
||||
// can be a problem is that older versions do an RTTI check that simply asks questions
|
||||
// like:
|
||||
//
|
||||
// Is there a member on this object called '__pulumiResource'
|
||||
//
|
||||
// If we automatically lift such a member (even if it eventually points to 'undefined'),
|
||||
// then those RTTI checks will succeed.
|
||||
//
|
||||
// Note: this should be safe to not lift as, in general, properties with this prefix
|
||||
// are not at all common (and in general are used to represent private things anyway
|
||||
// that likely should not be exposed).
|
||||
//
|
||||
// Similarly, do not respond to the 'doNotCapture' member name. It serves a similar
|
||||
// RTTI purpose.
|
||||
if (typeof prop === "string") {
|
||||
if (prop.startsWith("__") || prop === "doNotCapture" || prop === "deploymentOnlyModule") {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Fail out if we are being accessed using a symbol. Many APIs will access with a
|
||||
// well known symbol (like 'Symbol.toPrimitive') to check for the presence of something.
|
||||
// They will only check for the existence of that member, and we don't want to make it
|
||||
// appear that have those.
|
||||
//
|
||||
// Another way of putting this is that we only forward 'string/number' members to our
|
||||
// underlying value.
|
||||
if (typeof prop === "symbol") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Else for *any other* property lookup, succeed the lookup and return a lifted
|
||||
// `apply` on the underlying `Output`.
|
||||
return obj.apply(ob => {
|
||||
if (ob === undefined || ob === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return ob[prop];
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -376,14 +405,15 @@ function getResourcesAndIsKnown<T>(allOutputs: Output<Unwrap<T>>[]): [Resource[]
|
|||
}
|
||||
|
||||
/**
|
||||
* Input is a property input for a resource. It may be a promptly available T, a promise
|
||||
* for one, or the output from a existing Resource.
|
||||
* [Input] is a property input for a resource. It may be a promptly available T, a promise for one,
|
||||
* or the output from a existing Resource.
|
||||
*/
|
||||
export type Input<T> = T | Promise<T> | Output<T>;
|
||||
// Note: we accept an OutputInstance (and not an Output) here to be *more* flexible in terms of
|
||||
// what an Input is. OutputInstance has *less* members than Output (because it doesn't lift anything).
|
||||
export type Input<T> = T | Promise<T> | OutputInstance<T>;
|
||||
|
||||
/**
|
||||
* Inputs is a map of property name to property input, one for each resource
|
||||
* property value.
|
||||
* [Inputs] is a map of property name to property input, one for each resource property value.
|
||||
*/
|
||||
export type Inputs = Record<string, Input<any>>;
|
||||
|
||||
|
@ -416,7 +446,7 @@ export type Unwrap<T> =
|
|||
// 2. Otherwise, if we have an output, do the same as a promise and just unwrap the inner type.
|
||||
// 3. Otherwise, we have a basic type. Just unwrap that.
|
||||
T extends Promise<infer U1> ? UnwrapSimple<U1> :
|
||||
T extends Output<infer U2> ? UnwrapSimple<U2> :
|
||||
T extends OutputInstance<infer U2> ? UnwrapSimple<U2> :
|
||||
UnwrapSimple<T>;
|
||||
|
||||
type primitive = Function | string | number | boolean | undefined | null;
|
||||
|
@ -445,6 +475,178 @@ export type UnwrappedObject<T> = {
|
|||
[P in keyof T]: Unwrap<T[P]>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instance side of the [Output<T>] type. Exposes the deployment-time and run-time mechanisms
|
||||
* for working with the underlying value of an [Output<T>].
|
||||
*/
|
||||
export interface OutputInstance<T> {
|
||||
/* @internal */ readonly isKnown: Promise<boolean>;
|
||||
/* @internal */ promise(): Promise<T>;
|
||||
/* @internal */ resources(): Set<Resource>;
|
||||
|
||||
/**
|
||||
* Transforms the data of the output with the provided func. The result remains a
|
||||
* Output so that dependent resources can be properly tracked.
|
||||
*
|
||||
* 'func' is not allowed to make resources.
|
||||
*
|
||||
* 'func' can return other Outputs. This can be handy if you have a Output<SomeVal>
|
||||
* and you want to get a transitive dependency of it. i.e.
|
||||
*
|
||||
* ```ts
|
||||
* var d1: Output<SomeVal>;
|
||||
* var d2 = d1.apply(v => v.x.y.OtherOutput); // getting an output off of 'v'.
|
||||
* ```
|
||||
*
|
||||
* In this example, taking a dependency on d2 means a resource will depend on all the resources
|
||||
* of d1. It will *not* depend on the resources of v.x.y.OtherDep.
|
||||
*
|
||||
* Importantly, the Resources that d2 feels like it will depend on are the same resources as d1.
|
||||
* If you need have multiple Outputs and a single Output is needed that combines both
|
||||
* set of resources, then 'pulumi.all' should be used instead.
|
||||
*
|
||||
* This function will only be called execution of a 'pulumi update' request. It will not run
|
||||
* during 'pulumi preview' (as the values of resources are of course not known then). It is not
|
||||
* available for functions that end up executing in the cloud during runtime. To get the value
|
||||
* of the Output during cloud runtime execution, use `get()`.
|
||||
*/
|
||||
apply<U>(func: (t: T) => Input<U>): Output<U>;
|
||||
|
||||
/**
|
||||
* Retrieves the underlying value of this dependency.
|
||||
*
|
||||
* This function is only callable in code that runs in the cloud post-deployment. At this
|
||||
* point all Output values will be known and can be safely retrieved. During pulumi deployment
|
||||
* or preview execution this must not be called (and will throw). This is because doing so
|
||||
* would allow Output values to flow into Resources while losing the data that would allow
|
||||
* the dependency graph to be changed.
|
||||
*/
|
||||
get(): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static side of the [Output<T>] type. Can be used to [create] Outputs as well as test
|
||||
* arbitrary values to see if they are [Output]s.
|
||||
*/
|
||||
export interface OutputConstructor {
|
||||
create<T>(val: Input<T>): Output<Unwrap<T>>;
|
||||
create<T>(val: Input<T> | undefined): Output<Unwrap<T | undefined>>;
|
||||
|
||||
isInstance<T>(obj: any): obj is Output<T>;
|
||||
|
||||
/* @internal */ new<T>(
|
||||
resources: Set<Resource> | Resource[] | Resource,
|
||||
promise: Promise<T>,
|
||||
isKnown: Promise<boolean>): Output<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* [Output] helps encode the relationship between Resources in a Pulumi application. Specifically an
|
||||
* [Output] holds onto a piece of Data and the Resource it was generated from. An [Output] value can
|
||||
* then be provided when constructing new Resources, allowing that new Resource to know both the
|
||||
* value as well as the Resource the value came from. This allows for a precise 'Resource
|
||||
* dependency graph' to be created, which properly tracks the relationship between resources.
|
||||
*
|
||||
* An [Output] is used in a Pulumi program differently depending on if the application is executing
|
||||
* at 'deployment time' (i.e. when actually running the 'pulumi' executable), or at 'run time' (i.e.
|
||||
* a piece of code running in some Cloud).
|
||||
*
|
||||
* At 'deployment time', the correct way to work with the underlying value is to call
|
||||
* [Output.apply(func)]. This allows the value to be accessed and manipulated, while still
|
||||
* resulting in an [Output] that is keeping track of [Resource]s appropriately. At deployment time
|
||||
* the underlying value may or may not exist (for example, if a preview is being performed). In
|
||||
* this case, the 'func' callback will not be executed, and calling [.apply] will immediately return
|
||||
* an [Output] that points to the [undefined] value. During a normal [update] though, the 'func'
|
||||
* callbacks should always be executed.
|
||||
*
|
||||
* At 'run time', the correct way to work with the underlying value is to simply call [Output.get]
|
||||
* which will be promptly return the entire value. This will be a simple JavaScript object that can
|
||||
* be manipulated as necessary.
|
||||
*
|
||||
* To ease with using [Output]s at 'deployment time', pulumi will 'lift' simple data properties of
|
||||
* an underlying value to the [Output] itself. For example:
|
||||
*
|
||||
* ```ts
|
||||
* const o: Output<{ name: string, age: number, orders: Order[] }> = ...;
|
||||
* const name : Output<string> = o.name;
|
||||
* const age : Output<number> = o.age;
|
||||
* const first: Output<Order> = o.orders[0];
|
||||
* ```
|
||||
*
|
||||
* Instead of having to write:
|
||||
*
|
||||
* ```ts
|
||||
* const o: Output<{ name: string, age: number, orders: Order[] }> = ...;
|
||||
* const name : Output<string> = o.apply(v => v.name);
|
||||
* const age : Output<number> = o.apply(v => v.age);
|
||||
* const first: Output<Order> = o.apply(v => v.orders[0]);
|
||||
* ```
|
||||
*/
|
||||
export type Output<T> = OutputInstance<T> & Lifted<T>;
|
||||
// tslint:disable-next-line:variable-name
|
||||
export const Output: OutputConstructor = <any>OutputImpl;
|
||||
|
||||
/**
|
||||
* The [Lifted] type allows us to express the operation of taking a type, with potentially deeply
|
||||
* nested objects and arrays and to then get a type with the same properties, except whose property
|
||||
* types are now [Output]s of the original property type.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
*
|
||||
* `type X = { A: string, B: { c: boolean } }`
|
||||
*
|
||||
* Then `Lifted<X>` would be equivalent to:
|
||||
*
|
||||
* `... = { A: Output<string>, B: Output<{ C: Output<boolean> }> }`
|
||||
*
|
||||
* [Lifted] is somewhat the opposite of [Unwrap]. It's primary purpose is to allow an instance of
|
||||
* [Output<SomeType>] to provide simple access to the properties of [SomeType] directly on the instance
|
||||
* itself (instead of haveing to use [.apply]).
|
||||
*
|
||||
* This lifting only happens through simple pojo objects and arrays. Functions, for example, are not
|
||||
* lifted. So you cannot do:
|
||||
*
|
||||
* ```ts
|
||||
* const o: Output<string> = ...;
|
||||
* const c: Output<number> = o.charCodeAt(0);
|
||||
* ```
|
||||
*
|
||||
* Instead, you still need to write;
|
||||
*
|
||||
* ```ts
|
||||
* const o: Output<string> = ...;
|
||||
* const c: Output<number> = o.apply(v => v.charCodeAt(0));
|
||||
* ```
|
||||
*/
|
||||
export type Lifted<T> =
|
||||
// Output<T> is an intersection type with 'Lifted<T>'. So, when we don't want to add any
|
||||
// members to Output<T>, we just return `{}` which will leave it untouched.
|
||||
T extends Resource ? {} :
|
||||
// Specially handle 'string' since TS doesn't map the 'String.Length' property to it.
|
||||
T extends string ? LiftedObject<String, NonFunctionPropertyNames<String>> :
|
||||
T extends Array<infer U> ? LiftedArray<U> :
|
||||
LiftedObject<T, NonFunctionPropertyNames<T>>;
|
||||
|
||||
// The set of property names in T that are *not* functions.
|
||||
type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];
|
||||
|
||||
// Lift up all the non-function properties. If it was optional before, keep it optional after.
|
||||
// If it's require before, keep it required afterwards.
|
||||
export type LiftedObject<T, K extends keyof T> = {
|
||||
[P in K]: Output<T[P]>
|
||||
};
|
||||
|
||||
export type LiftedArray<T> = {
|
||||
/**
|
||||
* Gets the length of the array. This is a number one higher than the highest element defined
|
||||
* in an array.
|
||||
*/
|
||||
readonly length: Output<number>;
|
||||
|
||||
readonly [n: number]: Output<T>;
|
||||
};
|
||||
|
||||
/**
|
||||
* [concat] takes a sequence of [Inputs], stringifies each, and concatenates all values into one
|
||||
* final string. Individual inputs can be any sort of [Input] value. i.e. they can be [Promise]s,
|
||||
|
|
|
@ -30,6 +30,52 @@ export abstract class Resource {
|
|||
// tslint:disable-next-line:variable-name
|
||||
/* @internal */ public readonly __pulumiResource: boolean = true;
|
||||
|
||||
/**
|
||||
* The optional parent of this resource.
|
||||
*/
|
||||
// tslint:disable-next-line:variable-name
|
||||
/* @internal */ public readonly __parentResource: Resource | undefined;
|
||||
|
||||
/**
|
||||
* The child resources of this resource. We use these (only from a ComponentResource) to allow
|
||||
* code to dependOn a ComponentResource and have that effectively mean that it is depending on
|
||||
* all the CustomResource children of that component.
|
||||
*
|
||||
* Important! We only walk through ComponentResources. They're the only resources that serve
|
||||
* as an aggregation of other primitive (i.e. custom) resources. While a custom resource can be
|
||||
* a parent of other resources, we don't want to ever depend on those child resource. If we do,
|
||||
* it's simple to end up in a situation where we end up depending on a child resource that has a
|
||||
* data cycle dependency due to the data passed into it.
|
||||
*
|
||||
* An example of how this would be bad is:
|
||||
*
|
||||
* ```ts
|
||||
* var c1 = new CustomResource("c1");
|
||||
* var c2 = new CustomResource("c2", { parentId: c1.id }, { parent: c1 });
|
||||
* var c3 = new CustomResource("c3", { parentId: c1.id }, { parent: c1 });
|
||||
* ```
|
||||
*
|
||||
* The problem here is that 'c2' has a data dependency on 'c1'. If it tries to wait on 'c1' it
|
||||
* will walk to the children and wait on them. This will mean it will wait on 'c3'. But 'c3'
|
||||
* will be waiting in the same manner on 'c2', and a cycle forms.
|
||||
*
|
||||
* This normally does not happen with ComponentResources as they do not have any data flowing
|
||||
* into them. The only way you would be able to have a problem is if you had this sort of coding
|
||||
* pattern:
|
||||
*
|
||||
* ```ts
|
||||
* var c1 = new ComponentResource("c1");
|
||||
* var c2 = new CustomResource("c2", { parentId: c1.urn }, { parent: c1 });
|
||||
* var c3 = new CustomResource("c3", { parentId: c1.urn }, { parent: c1 });
|
||||
* ```
|
||||
*
|
||||
* However, this would be pretty nonsensical as there is zero need for a custom resource to ever
|
||||
* need to reference the urn of a component resource. So it's acceptable if that sort of
|
||||
* pattern failed in practice.
|
||||
*/
|
||||
// tslint:disable-next-line:variable-name
|
||||
/* @internal */ public __childResources: Set<Resource> | undefined;
|
||||
|
||||
/**
|
||||
* urn is the stable logical URN used to distinctly address a resource, both before and after
|
||||
* deployments.
|
||||
|
@ -90,6 +136,10 @@ export abstract class Resource {
|
|||
throw new RunError(`Resource parent is not a valid Resource: ${opts.parent}`);
|
||||
}
|
||||
|
||||
this.__parentResource = opts.parent;
|
||||
this.__parentResource.__childResources = this.__parentResource.__childResources || new Set();
|
||||
this.__parentResource.__childResources.add(this);
|
||||
|
||||
if (opts.protect === undefined) {
|
||||
opts.protect = opts.parent.__protect;
|
||||
}
|
||||
|
@ -263,6 +313,20 @@ export abstract class ProviderResource extends CustomResource {
|
|||
* operations for provisioning.
|
||||
*/
|
||||
export class ComponentResource extends Resource {
|
||||
/**
|
||||
* A private field to help with RTTI that works in SxS scenarios.
|
||||
*/
|
||||
// tslint:disable-next-line:variable-name
|
||||
/* @internal */ public readonly __pulumiComponentResource: boolean;
|
||||
|
||||
/**
|
||||
* Returns true if the given object is an instance of CustomResource. This is designed to work even when
|
||||
* multiple copies of the Pulumi SDK have been loaded into the same process.
|
||||
*/
|
||||
public static isInstance(obj: any): obj is ComponentResource {
|
||||
return utils.isInstance<ComponentResource>(obj, "__pulumiComponentResource");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and registers a new component resource. [type] is the fully qualified type token and
|
||||
* [name] is the "name" part to use in creating a stable and globally unique URN for the object.
|
||||
|
@ -290,6 +354,7 @@ export class ComponentResource extends Resource {
|
|||
// not correspond to a real piece of cloud infrastructure. As such, changes to it *itself*
|
||||
// do not have any effect on the cloud side of things at all.
|
||||
super(type, name, /*custom:*/ false, /*props:*/ {}, opts);
|
||||
this.__pulumiComponentResource = true;
|
||||
}
|
||||
|
||||
// registerOutputs registers synthetic outputs that a component has initialized, usually by
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
import * as grpc from "grpc";
|
||||
import * as log from "../log";
|
||||
import { Input, Inputs, Output } from "../output";
|
||||
import { CustomResourceOptions, ID, Resource, ResourceOptions, URN } from "../resource";
|
||||
import { ComponentResource, CustomResource, CustomResourceOptions, ID, Resource, ResourceOptions, URN } from "../resource";
|
||||
import { debuggablePromise } from "./debuggable";
|
||||
|
||||
import {
|
||||
|
@ -47,10 +47,12 @@ interface ResourceResolverOperation {
|
|||
providerRef: string | undefined;
|
||||
// All serialized properties, fully awaited, serialized, and ready to go.
|
||||
serializedProps: Record<string, any>;
|
||||
// A set of URNs that this resource is directly dependent upon.
|
||||
// A set of URNs that this resource is directly dependent upon. These will all be URNs of
|
||||
// custom resources, not component resources.
|
||||
allDirectDependencyURNs: Set<URN>;
|
||||
// Set of URNs that this resource is directly dependent upon, keyed by the property that
|
||||
// causes the dependency. All urns in this map must exist in [allDirectDependencyURNs]
|
||||
// Set of URNs that this resource is directly dependent upon, keyed by the property that causes
|
||||
// the dependency. All urns in this map must exist in [allDirectDependencyURNs]. These will
|
||||
// all be URNs of custom resources, not component resources.
|
||||
propertyToDirectDependencyURNs: Map<string, Set<URN>>;
|
||||
}
|
||||
|
||||
|
@ -258,13 +260,13 @@ async function prepareResource(label: string, res: Resource, custom: boolean,
|
|||
// The list of all dependencies (implicit or explicit).
|
||||
const allDirectDependencies = new Set<Resource>(explicitDirectDependencies);
|
||||
|
||||
const allDirectDependencyURNs = await getResourceURNs(explicitDirectDependencies);
|
||||
const allDirectDependencyURNs = await getAllTransitivelyReferencedCustomResourceURNs(explicitDirectDependencies);
|
||||
const propertyToDirectDependencyURNs = new Map<string, Set<URN>>();
|
||||
|
||||
for (const [propertyName, directDependencies] of propertyToDirectDependencies) {
|
||||
addAll(allDirectDependencies, directDependencies);
|
||||
|
||||
const urns = await getResourceURNs(directDependencies);
|
||||
const urns = await getAllTransitivelyReferencedCustomResourceURNs(directDependencies);
|
||||
addAll(allDirectDependencyURNs, urns);
|
||||
propertyToDirectDependencyURNs.set(propertyName, urns);
|
||||
}
|
||||
|
@ -287,15 +289,60 @@ function addAll<T>(to: Set<T>, from: Set<T>) {
|
|||
}
|
||||
}
|
||||
|
||||
async function getResourceURNs(resources: Set<Resource>) {
|
||||
const result = new Set<URN>();
|
||||
for (const resource of resources) {
|
||||
result.add(await resource.urn.promise());
|
||||
}
|
||||
async function getAllTransitivelyReferencedCustomResourceURNs(resources: Set<Resource>) {
|
||||
// Go through 'resources', but transitively walk through **Component** resources, collecting any
|
||||
// of their child resources. This way, a Component acts as an aggregation really of all the
|
||||
// reachable custom resources it parents. This walking will transitively walk through other
|
||||
// child ComponentResources, but will stop when it hits custom resources. in other words, if we
|
||||
// had:
|
||||
//
|
||||
// Comp1
|
||||
// / \
|
||||
// Cust1 Comp2
|
||||
// / \
|
||||
// Cust2 Cust3
|
||||
// /
|
||||
// Cust4
|
||||
//
|
||||
// Then the transitively reachable custom resources of Comp1 will be [Cust1, Cust2, Cust3]. It
|
||||
// will *not* include `Cust4`.
|
||||
|
||||
// To do this, first we just get the transitively reachable set of resources (not diving
|
||||
// into custom resources). In the above picture, if we start with 'Comp1', this will be
|
||||
// [Comp1, Cust1, Comp2, Cust2, Cust3]
|
||||
const transitivelyReachableResources = getTransitivelyReferencedChildResourcesOfComponentResources(resources);
|
||||
|
||||
const transitivelyReacableCustomResources = [...transitivelyReachableResources].filter(r => CustomResource.isInstance(r));
|
||||
const promises = transitivelyReacableCustomResources.map(r => r.urn.promise());
|
||||
const urns = await Promise.all(promises);
|
||||
return new Set<string>(urns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively walk the resources passed in, returning them and all resources reachable from
|
||||
* [Resource.__childResources] through any **Component** resources we encounter.
|
||||
*/
|
||||
function getTransitivelyReferencedChildResourcesOfComponentResources(resources: Set<Resource>) {
|
||||
// Recursively walk the dependent resources through their children, adding them to the result set.
|
||||
const result = new Set<Resource>();
|
||||
addTransitivelyReferencedChildResourcesOfComponentResources(resources, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
function addTransitivelyReferencedChildResourcesOfComponentResources(resources: Set<Resource> | undefined, result: Set<Resource>) {
|
||||
if (resources) {
|
||||
for (const resource of resources) {
|
||||
if (!result.has(resource)) {
|
||||
result.add(resource);
|
||||
|
||||
if (ComponentResource.isInstance(resource)) {
|
||||
addTransitivelyReferencedChildResourcesOfComponentResources(resource.__childResources, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers explicit dependent Resources from a list of Resources (possibly Promises and/or Outputs).
|
||||
*/
|
||||
|
|
|
@ -19,6 +19,25 @@ import { Output, concat, interpolate, output } from "../output";
|
|||
import * as runtime from "../runtime";
|
||||
import { asyncTest } from "./util";
|
||||
|
||||
interface Widget {
|
||||
type: string; // metric | text
|
||||
x?: number;
|
||||
y?: number;
|
||||
properties: Object;
|
||||
}
|
||||
|
||||
// This ensures that the optionality of 'x' and 'y' are preserved when describing an Output<Widget>.
|
||||
// Subtle changes in the definitions of Lifted<T> can make TS think these are required, which can
|
||||
// break downstream consumers.
|
||||
function mustCompile(): Output<Widget> {
|
||||
return output({
|
||||
type: "foo",
|
||||
properties: {
|
||||
whatever: 1,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
describe("output", () => {
|
||||
it("propagates true isKnown bit from inner Output", asyncTest(async () => {
|
||||
runtime.setIsDryRun(true);
|
||||
|
@ -128,4 +147,45 @@ describe("output", () => {
|
|||
assert.equal(await result.promise(), "http://a:80/");
|
||||
}));
|
||||
});
|
||||
|
||||
describe("lifted operations", () => {
|
||||
it("lifts properties from inner object", asyncTest(async () => {
|
||||
const output1 = output({ a: 1, b: true, c: "str", d: [2], e: { f: 3 }, g: undefined, h: null });
|
||||
|
||||
assert.equal(await output1.a.promise(), 1);
|
||||
assert.equal(await output1.b.promise(), true);
|
||||
assert.equal(await output1.c.promise(), "str");
|
||||
|
||||
// Can lift both outer arrays as well as array accesses
|
||||
assert.deepEqual(await output1.d.promise(), [2]);
|
||||
assert.equal(await output1.d[0].promise(), 2);
|
||||
|
||||
// Can lift nested objects as well as their properties.
|
||||
assert.deepEqual(await output1.e.promise(), { f: 3 });
|
||||
assert.equal(await output1.e.f.promise(), 3);
|
||||
|
||||
assert.strictEqual(await output1.g.promise(), undefined);
|
||||
assert.strictEqual(await output1.h.promise(), null);
|
||||
|
||||
// Unspecified things can be lifted, but produce 'undefined'.
|
||||
assert.notEqual((<any>output1).z, undefined);
|
||||
assert.equal(await (<any>output1).z.promise(), undefined);
|
||||
}));
|
||||
|
||||
it("prefers Output members over lifted members", asyncTest(async () => {
|
||||
const output1 = output({ apply: 1, promise: 2 });
|
||||
assert.ok(output1.apply instanceof Function);
|
||||
assert.ok(output1.isKnown instanceof Promise);
|
||||
}));
|
||||
|
||||
it("does not lift symbols", asyncTest(async () => {
|
||||
const output1 = output({ apply: 1, promise: 2 });
|
||||
assert.strictEqual((<any>output1)[Symbol.toPrimitive], undefined);
|
||||
}));
|
||||
|
||||
it("does not lift __ properties", asyncTest(async () => {
|
||||
const output1 = output({ a: 1, b: 2 });
|
||||
assert.strictEqual((<any>output1).__pulumiResource, undefined);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// Test the ability to invoke provider functions via RPC.
|
||||
|
||||
let assert = require("assert");
|
||||
let pulumi = require("../../../../../");
|
||||
|
||||
|
@ -9,5 +7,9 @@ class MyResource extends pulumi.CustomResource {
|
|||
}
|
||||
}
|
||||
|
||||
let resA = new MyResource("resA", {});
|
||||
let resB = new MyResource("resB", { parentId: resA.id }, { parent: resA });
|
||||
// cust1
|
||||
// \
|
||||
// cust2
|
||||
|
||||
let cust1 = new MyResource("cust1", {});
|
||||
let cust2 = new MyResource("cust2", { parentId: cust1.id }, { parent: cust1 });
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// Test the ability to invoke provider functions via RPC.
|
||||
|
||||
let assert = require("assert");
|
||||
let pulumi = require("../../../../../");
|
||||
|
||||
|
@ -9,6 +7,10 @@ class MyResource extends pulumi.CustomResource {
|
|||
}
|
||||
}
|
||||
|
||||
let resA = new MyResource("resA");
|
||||
let resB = new MyResource("resB", { parentId: resA.id }, { parent: resA });
|
||||
let resC = new MyResource("resC", { parentId: resA.id }, { parent: resA });
|
||||
// cust1
|
||||
// / \
|
||||
// cust2 cust3
|
||||
|
||||
let cust1 = new MyResource("cust1");
|
||||
let cust2 = new MyResource("cust2", { parentId: cust1.id }, { parent: cust1 });
|
||||
let cust3 = new MyResource("cust3", { parentId: cust1.id }, { parent: cust1 });
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
let assert = require("assert");
|
||||
let pulumi = require("../../../../../");
|
||||
|
||||
class MyCustomResource extends pulumi.CustomResource {
|
||||
constructor(name, args, opts) {
|
||||
super("test:index:MyCustomResource", name, args, opts);
|
||||
}
|
||||
}
|
||||
|
||||
class MyComponentResource extends pulumi.ComponentResource {
|
||||
constructor(name, args, opts) {
|
||||
super("test:index:MyComponentResource", name, args, opts);
|
||||
}
|
||||
}
|
||||
|
||||
// comp1
|
||||
// / \
|
||||
// cust1 cust2
|
||||
|
||||
let comp1 = new MyComponentResource("comp1");
|
||||
|
||||
// this represents a nonsensical program where a custom resource references the urn
|
||||
// of a component. There is no good need for the urn to be used there. To represent
|
||||
// a component dependency, 'dependsOn' should be used instead.
|
||||
//
|
||||
// This test just documents our behavior here (which is that we deadlock).
|
||||
let cust1 = new MyCustomResource("cust1", { parentId: comp1.urn }, { parent: comp1 });
|
||||
let cust2 = new MyCustomResource("cust2", { parentId: comp1.urn }, { parent: comp1 });
|
|
@ -0,0 +1,22 @@
|
|||
let assert = require("assert");
|
||||
let pulumi = require("../../../../../");
|
||||
|
||||
class MyCustomResource extends pulumi.CustomResource {
|
||||
constructor(name, args, opts) {
|
||||
super("test:index:MyCustomResource", name, args, opts);
|
||||
}
|
||||
}
|
||||
|
||||
class MyComponentResource extends pulumi.ComponentResource {
|
||||
constructor(name, args, opts) {
|
||||
super("test:index:MyComponentResource", name, args, opts);
|
||||
}
|
||||
}
|
||||
|
||||
// comp1
|
||||
// / \
|
||||
// cust1 cust2
|
||||
|
||||
let comp1 = new MyComponentResource("comp1");
|
||||
let cust1 = new MyCustomResource("cust1", { }, { parent: comp1 });
|
||||
let cust2 = new MyCustomResource("cust2", { }, { parent: comp1 });
|
|
@ -0,0 +1,24 @@
|
|||
let assert = require("assert");
|
||||
let pulumi = require("../../../../../");
|
||||
|
||||
class MyCustomResource extends pulumi.CustomResource {
|
||||
constructor(name, args, opts) {
|
||||
super("test:index:MyCustomResource", name, args, opts);
|
||||
}
|
||||
}
|
||||
|
||||
class MyComponentResource extends pulumi.ComponentResource {
|
||||
constructor(name, args, opts) {
|
||||
super("test:index:MyComponentResource", name, args, opts);
|
||||
}
|
||||
}
|
||||
|
||||
// comp1
|
||||
// / \
|
||||
// cust1 cust2
|
||||
|
||||
let comp1 = new MyComponentResource("comp1");
|
||||
let cust1 = new MyCustomResource("cust1", { }, { parent: comp1 });
|
||||
let cust2 = new MyCustomResource("cust2", { }, { parent: comp1 });
|
||||
|
||||
let res1 = new MyCustomResource("res1", { }, { dependsOn: comp1 });
|
|
@ -0,0 +1,29 @@
|
|||
let assert = require("assert");
|
||||
let pulumi = require("../../../../../");
|
||||
|
||||
class MyCustomResource extends pulumi.CustomResource {
|
||||
constructor(name, args, opts) {
|
||||
super("test:index:MyCustomResource", name, args, opts);
|
||||
}
|
||||
}
|
||||
|
||||
class MyComponentResource extends pulumi.ComponentResource {
|
||||
constructor(name, args, opts) {
|
||||
super("test:index:MyComponentResource", name, args, opts);
|
||||
}
|
||||
}
|
||||
|
||||
// comp1
|
||||
// / \
|
||||
// cust1 comp2
|
||||
// / \
|
||||
// cust2 cust3
|
||||
|
||||
let comp1 = new MyComponentResource("comp1");
|
||||
let cust1 = new MyCustomResource("cust1", { }, { parent: comp1 });
|
||||
|
||||
let comp2 = new MyComponentResource("comp2", { }, { parent: comp1 });
|
||||
let cust2 = new MyCustomResource("cust2", { }, { parent: comp2 });
|
||||
let cust3 = new MyCustomResource("cust3", { }, { parent: comp2 });
|
||||
|
||||
let res1 = new MyCustomResource("res1", { }, { dependsOn: comp1 });
|
|
@ -0,0 +1,36 @@
|
|||
let assert = require("assert");
|
||||
let pulumi = require("../../../../../");
|
||||
|
||||
class MyCustomResource extends pulumi.CustomResource {
|
||||
constructor(name, args, opts) {
|
||||
super("test:index:MyCustomResource", name, args, opts);
|
||||
}
|
||||
}
|
||||
|
||||
class MyComponentResource extends pulumi.ComponentResource {
|
||||
constructor(name, args, opts) {
|
||||
super("test:index:MyComponentResource", name, args, opts);
|
||||
}
|
||||
}
|
||||
|
||||
// comp1
|
||||
// / \
|
||||
// cust1 comp2
|
||||
// / \
|
||||
// cust2 cust3
|
||||
// /
|
||||
// cust4
|
||||
|
||||
let comp1 = new MyComponentResource("comp1");
|
||||
let cust1 = new MyCustomResource("cust1", { }, { parent: comp1 });
|
||||
|
||||
let comp2 = new MyComponentResource("comp2", { }, { parent: comp1 });
|
||||
let cust2 = new MyCustomResource("cust2", { }, { parent: comp2 });
|
||||
let cust3 = new MyCustomResource("cust3", { }, { parent: comp2 });
|
||||
|
||||
let cust4 = new MyCustomResource("cust4", { parentId: cust2.id }, { parent: cust2 });
|
||||
|
||||
let res1 = new MyCustomResource("res1", { }, { dependsOn: comp1 });
|
||||
let res2 = new MyCustomResource("res2", { }, { dependsOn: comp2 });
|
||||
let res3 = new MyCustomResource("res3", { }, { dependsOn: cust2 });
|
||||
let res4 = new MyCustomResource("res4", { }, { dependsOn: cust4 });
|
|
@ -0,0 +1,28 @@
|
|||
let assert = require("assert");
|
||||
let pulumi = require("../../../../../");
|
||||
|
||||
class MyCustomResource extends pulumi.CustomResource {
|
||||
constructor(name, args, opts) {
|
||||
super("test:index:MyCustomResource", name, args, opts);
|
||||
}
|
||||
}
|
||||
|
||||
class MyComponentResource extends pulumi.ComponentResource {
|
||||
constructor(name, args, opts) {
|
||||
super("test:index:MyComponentResource", name, args, opts);
|
||||
}
|
||||
}
|
||||
|
||||
// comp1
|
||||
// \
|
||||
// cust1
|
||||
// \
|
||||
// cust2
|
||||
|
||||
let comp1 = new MyComponentResource("comp1");
|
||||
let cust1 = new MyCustomResource("cust1", { }, { parent: comp1 });
|
||||
let cust2 = new MyCustomResource("cust2", { parentId: cust1.id }, { parent: cust1 });
|
||||
|
||||
let res1 = new MyCustomResource("res1", { }, { dependsOn: comp1 });
|
||||
let res2 = new MyCustomResource("res2", { }, { dependsOn: cust1 });
|
||||
let res3 = new MyCustomResource("res3", { }, { dependsOn: cust2 });
|
|
@ -0,0 +1,23 @@
|
|||
let assert = require("assert");
|
||||
let pulumi = require("../../../../../");
|
||||
|
||||
class MyCustomResource extends pulumi.CustomResource {
|
||||
constructor(name, args, opts) {
|
||||
super("test:index:MyCustomResource", name, args, opts);
|
||||
}
|
||||
}
|
||||
|
||||
class MyComponentResource extends pulumi.ComponentResource {
|
||||
constructor(name, args, opts) {
|
||||
super("test:index:MyComponentResource", name, args, opts);
|
||||
}
|
||||
}
|
||||
|
||||
// cust1
|
||||
// \
|
||||
// cust2
|
||||
|
||||
let cust1 = new MyCustomResource("cust1", { }, { });
|
||||
let cust2 = new MyCustomResource("cust2", { parentId: cust1.id }, { parent: cust1 });
|
||||
|
||||
let res1 = new MyCustomResource("res1", { }, { dependsOn: cust1 });
|
|
@ -600,7 +600,12 @@ describe("rpc", () => {
|
|||
pwd: path.join(base, "021.parent_child_dependencies"),
|
||||
program: "./index.js",
|
||||
expectResourceCount: 2,
|
||||
registerResource: (ctx: any, dryrun: boolean, t: string, name: string) => {
|
||||
registerResource: (ctx: any, dryrun: boolean, t: string, name: string, res: any, deps: string[]) => {
|
||||
switch (name) {
|
||||
case "cust1": assert.deepStrictEqual(deps, []); break;
|
||||
case "cust2": assert.deepStrictEqual(deps, ["test:index:MyResource::cust1"]); break;
|
||||
default: throw new Error("Didn't check: " + name);
|
||||
}
|
||||
return { urn: makeUrn(t, name), id: undefined, props: undefined };
|
||||
},
|
||||
},
|
||||
|
@ -608,14 +613,124 @@ describe("rpc", () => {
|
|||
pwd: path.join(base, "022.parent_child_dependencies_2"),
|
||||
program: "./index.js",
|
||||
expectResourceCount: 3,
|
||||
registerResource: (ctx: any, dryrun: boolean, t: string, name: string) => {
|
||||
registerResource: (ctx: any, dryrun: boolean, t: string, name: string, res: any, deps: string[]) => {
|
||||
switch (name) {
|
||||
case "cust1": assert.deepStrictEqual(deps, []); break;
|
||||
case "cust2": assert.deepStrictEqual(deps, ["test:index:MyResource::cust1"]); break;
|
||||
case "cust3": assert.deepStrictEqual(deps, ["test:index:MyResource::cust1"]); break;
|
||||
default: throw new Error("Didn't check: " + name);
|
||||
}
|
||||
return { urn: makeUrn(t, name), id: undefined, props: undefined };
|
||||
},
|
||||
},
|
||||
"parent_child_dependencies_3": {
|
||||
pwd: path.join(base, "023.parent_child_dependencies_3"),
|
||||
program: "./index.js",
|
||||
expectResourceCount: 1,
|
||||
expectError: "Program exited with non-zero exit code: 1",
|
||||
},
|
||||
"parent_child_dependencies_4": {
|
||||
pwd: path.join(base, "024.parent_child_dependencies_4"),
|
||||
program: "./index.js",
|
||||
expectResourceCount: 3,
|
||||
registerResource: (ctx: any, dryrun: boolean, t: string, name: string, res: any, deps: string[]) => {
|
||||
switch (name) {
|
||||
case "cust1": assert.deepStrictEqual(deps, []); break;
|
||||
case "cust2": assert.deepStrictEqual(deps, []); break;
|
||||
case "comp1": assert.deepStrictEqual(deps, []); break;
|
||||
default: throw new Error("Didn't check: " + name);
|
||||
}
|
||||
return { urn: makeUrn(t, name), id: undefined, props: undefined };
|
||||
},
|
||||
},
|
||||
"parent_child_dependencies_5": {
|
||||
pwd: path.join(base, "025.parent_child_dependencies_5"),
|
||||
program: "./index.js",
|
||||
expectResourceCount: 4,
|
||||
registerResource: (ctx: any, dryrun: boolean, t: string, name: string, res: any, deps: string[]) => {
|
||||
switch (name) {
|
||||
case "cust1": assert.deepStrictEqual(deps, []); break;
|
||||
case "cust2": assert.deepStrictEqual(deps, []); break;
|
||||
case "comp1": assert.deepStrictEqual(deps, []); break;
|
||||
case "res1": assert.deepStrictEqual(deps, ["test:index:MyCustomResource::cust1", "test:index:MyCustomResource::cust2"]); break;
|
||||
default: throw new Error("Didn't check: " + name);
|
||||
}
|
||||
return { urn: makeUrn(t, name), id: undefined, props: undefined };
|
||||
},
|
||||
},
|
||||
"parent_child_dependencies_6": {
|
||||
pwd: path.join(base, "026.parent_child_dependencies_6"),
|
||||
program: "./index.js",
|
||||
expectResourceCount: 6,
|
||||
registerResource: (ctx: any, dryrun: boolean, t: string, name: string, res: any, deps: string[]) => {
|
||||
switch (name) {
|
||||
case "comp1": assert.deepStrictEqual(deps, []); break;
|
||||
case "cust1": assert.deepStrictEqual(deps, []); break;
|
||||
case "comp2": assert.deepStrictEqual(deps, []); break;
|
||||
case "cust2": assert.deepStrictEqual(deps, []); break;
|
||||
case "cust3": assert.deepStrictEqual(deps, []); break;
|
||||
case "res1": assert.deepStrictEqual(deps, ["test:index:MyCustomResource::cust1", "test:index:MyCustomResource::cust2", "test:index:MyCustomResource::cust3"]); break;
|
||||
default: throw new Error("Didn't check: " + name);
|
||||
}
|
||||
return { urn: makeUrn(t, name), id: undefined, props: undefined };
|
||||
},
|
||||
},
|
||||
"parent_child_dependencies_7": {
|
||||
pwd: path.join(base, "027.parent_child_dependencies_7"),
|
||||
program: "./index.js",
|
||||
expectResourceCount: 10,
|
||||
registerResource: (ctx: any, dryrun: boolean, t: string, name: string, res: any, deps: string[]) => {
|
||||
switch (name) {
|
||||
case "comp1": assert.deepStrictEqual(deps, []); break;
|
||||
case "cust1": assert.deepStrictEqual(deps, []); break;
|
||||
case "comp2": assert.deepStrictEqual(deps, []); break;
|
||||
case "cust2": assert.deepStrictEqual(deps, []); break;
|
||||
case "cust3": assert.deepStrictEqual(deps, []); break;
|
||||
case "cust4": assert.deepStrictEqual(deps, ["test:index:MyCustomResource::cust2"]); break;
|
||||
case "res1": assert.deepStrictEqual(deps, ["test:index:MyCustomResource::cust1", "test:index:MyCustomResource::cust2", "test:index:MyCustomResource::cust3"]); break;
|
||||
case "res2": assert.deepStrictEqual(deps, ["test:index:MyCustomResource::cust2", "test:index:MyCustomResource::cust3"]); break;
|
||||
case "res3": assert.deepStrictEqual(deps, ["test:index:MyCustomResource::cust2"]); break;
|
||||
case "res4": assert.deepStrictEqual(deps, ["test:index:MyCustomResource::cust4"]); break;
|
||||
default: throw new Error("Didn't check: " + name);
|
||||
}
|
||||
return { urn: makeUrn(t, name), id: undefined, props: undefined };
|
||||
},
|
||||
},
|
||||
"parent_child_dependencies_8": {
|
||||
pwd: path.join(base, "028.parent_child_dependencies_8"),
|
||||
program: "./index.js",
|
||||
expectResourceCount: 6,
|
||||
registerResource: (ctx: any, dryrun: boolean, t: string, name: string, res: any, deps: string[]) => {
|
||||
switch (name) {
|
||||
case "comp1": assert.deepStrictEqual(deps, []); break;
|
||||
case "cust1": assert.deepStrictEqual(deps, []); break;
|
||||
case "cust2": assert.deepStrictEqual(deps, ["test:index:MyCustomResource::cust1"]); break;
|
||||
case "res1": assert.deepStrictEqual(deps, ["test:index:MyCustomResource::cust1"]); break;
|
||||
case "res2": assert.deepStrictEqual(deps, ["test:index:MyCustomResource::cust1"]); break;
|
||||
case "res3": assert.deepStrictEqual(deps, ["test:index:MyCustomResource::cust2"]); break;
|
||||
default: throw new Error("Didn't check: " + name);
|
||||
}
|
||||
return { urn: makeUrn(t, name), id: undefined, props: undefined };
|
||||
},
|
||||
},
|
||||
"parent_child_dependencies_9": {
|
||||
pwd: path.join(base, "029.parent_child_dependencies_9"),
|
||||
program: "./index.js",
|
||||
expectResourceCount: 3,
|
||||
registerResource: (ctx: any, dryrun: boolean, t: string, name: string, res: any, deps: string[]) => {
|
||||
switch (name) {
|
||||
case "cust1": assert.deepStrictEqual(deps, []); break;
|
||||
case "cust2": assert.deepStrictEqual(deps, ["test:index:MyCustomResource::cust1"]); break;
|
||||
case "res1": assert.deepStrictEqual(deps, ["test:index:MyCustomResource::cust1"]); break;
|
||||
default: throw new Error("Didn't check: " + name);
|
||||
}
|
||||
return { urn: makeUrn(t, name), id: undefined, props: undefined };
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
for (const casename of Object.keys(cases)) {
|
||||
// if (casename !== "parent_child_dependencies_2") {
|
||||
// if (casename.indexOf("parent_child_dependencies") < 0) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
|
|
|
@ -5358,15 +5358,13 @@ return function () { typescript.parseCommandLine([""]); };
|
|||
func: function () { console.log(pulumi); },
|
||||
error: `Error serializing function 'func': tsClosureCases.js(0,0)
|
||||
|
||||
function 'func': tsClosureCases.js(0,0): captured
|
||||
function 'func':(...)
|
||||
module './bin/index.js' which indirectly referenced
|
||||
function 'debug':(...)
|
||||
module './bin/runtime/settings.js' which indirectly referenced
|
||||
function 'getEngine':(...)
|
||||
module './bin/proto/engine_grpc_pb.js' which indirectly referenced
|
||||
(...)
|
||||
Function code:
|
||||
(...)
|
||||
function [Symbol.hasInstance]() { [native code] }
|
||||
|
||||
Module './bin/index.js' is a 'deployment only' module. In general these cannot be captured inside a 'run time' function.`
|
||||
});
|
||||
}
|
||||
|
|
|
@ -134,9 +134,9 @@ describe("unwrap", () => {
|
|||
});
|
||||
|
||||
function createOutput<T>(cv: T, ...resources: TestResource[]): Output<T> {
|
||||
return Output.isInstance(cv)
|
||||
return Output.isInstance<T>(cv)
|
||||
? cv
|
||||
: new Output<any>(<any>new Set(resources), Promise.resolve(cv), Promise.resolve(true))
|
||||
: new Output(<any>new Set(resources), Promise.resolve(cv), Promise.resolve(true))
|
||||
}
|
||||
|
||||
describe("preserves resources", () => {
|
||||
|
|
Loading…
Reference in a new issue