Approach without providerrefs.

This commit is contained in:
Cyrus Najmabadi 2019-10-14 13:19:37 -07:00
parent 1e759dcc69
commit 009a7cc428
4 changed files with 39 additions and 120 deletions

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ProviderResourceOrRef, Resource } from "./resource";
import { ProviderResource, Resource } from "./resource";
/*
* InvokeOptions is a bag of options that control the behavior of a call to runtime.invoke.
@ -27,7 +27,7 @@ export interface InvokeOptions {
* An optional provider to use for this invocation. If no provider is supplied, the default provider for the
* invoked function's package will be used.
*/
provider?: ProviderResourceOrRef;
provider?: ProviderResource;
/**
* An optional version, corresponding to the version of the provider plugin that should be used when performing this

View file

@ -179,14 +179,14 @@ export abstract class Resource {
* The set of providers to use for child resources. Keyed by package name (e.g. "aws").
*/
// tslint:disable-next-line:variable-name
private readonly __providers: Record<string, ProviderResourceOrRef>;
private readonly __providers: Record<string, ProviderResource>;
public static isInstance(obj: any): obj is Resource {
return utils.isInstance<Resource>(obj, "__pulumiResource");
}
// getProvider fetches the provider for the given module member, if any.
public getProvider(moduleMember: string): ProviderResourceOrRef | undefined {
public getProvider(moduleMember: string): ProviderResource | undefined {
const memComponents = moduleMember.split(":");
if (memComponents.length !== 3) {
return undefined;
@ -313,7 +313,7 @@ export abstract class Resource {
}
}
function convertToProvidersMap(providers: Record<string, ProviderResourceOrRef> | ProviderResourceOrRef[] | undefined) {
function convertToProvidersMap(providers: Record<string, ProviderResource> | ProviderResource[] | undefined) {
if (!providers) {
return {};
}
@ -322,7 +322,7 @@ function convertToProvidersMap(providers: Record<string, ProviderResourceOrRef>
return providers;
}
const result: Record<string, ProviderResourceOrRef> = {};
const result: Record<string, ProviderResource> = {};
for (const provider of providers) {
result[provider.getPackage()] = provider;
}
@ -477,7 +477,7 @@ export interface ResourceOptions {
*
* If this is a [ComponentResourceOptions] do not provide both [provider] and [providers]
*/
provider?: ProviderResourceOrRef;
provider?: ProviderResource;
/**
* An optional customTimeouts configuration block.
*/
@ -608,7 +608,7 @@ export interface ComponentResourceOptions extends ResourceOptions {
*
* Note: do not provide both [provider] and [providers];
*/
providers?: Record<string, ProviderResourceOrRef> | ProviderResourceOrRef[];
providers?: Record<string, ProviderResource> | ProviderResource[];
// !!! IMPORTANT !!! If you add a new field to this type, make sure to add test that verifies
// that mergeOptions works properly for it.
@ -684,6 +684,22 @@ export abstract class ProviderResource extends CustomResource {
/** @internal */
private readonly pkg: string;
/** @internal */
// tslint:disable-next-line: variable-name
public __registrationId?: string;
public static async register(provider: ProviderResource | undefined): Promise<string | undefined> {
if (provider === undefined) {
return undefined;
}
if (!provider.__registrationId) {
provider.__registrationId = `${await provider.urn}::${await provider.id}`;
}
return provider.__registrationId;
}
/**
* Creates and registers a new provider resource for a particular package.
*
@ -703,82 +719,6 @@ export abstract class ProviderResource extends CustomResource {
}
}
/**
* A reference to a `ProviderResource`. Should be used as much as possible when referring to
* `Provider`s from other locations. For example, when specifying the `Provider` for a `Resource`
* or the `Provider` for an `data source` `invoke` call, a `ProviderRef` should be provided instead
* of a `Provider`.
*
* A `ProviderRef` can be obtained by calling `await ProviderRef.get(provider)`.
*/
export class ProviderRef {
/**
* @internal
* A private field to help with RTTI that works in SxS scenarios.
*/
// tslint:disable-next-line: variable-name
public readonly __providerRefInstance = true;
/** @internal */
public getPackage: () => string;
/** @internal */
public getValue: () => string;
// For SxS we make a ProviderRef *look* like a Provider. i.e. we expose both `urn` and `id`
// directly off of it (just like they are on Provider). This way if a ProviderRef is passed to
// an older `@pulumi/pulumi` then the older `@pulumi/pulumi` will still be able to do:
// https://github.com/pulumi/pulumi/blob/756534865edd64ab9d790575355cfea056d2b718/sdk/nodejs/runtime/invoke.ts#L52-L56
//
// ```ts
// if (opts.provider !== undefined) {
// const providerURN = await opts.provider.urn.promise();
// const providerID = await opts.provider.id.promise() || unknownValue;
// providerRef = `${providerURN}::${providerID}`;
// }
// ```
/**
* urn is the stable logical URN used to distinctly address the Provider, both before and after
* deployments.
*/
public readonly urn: Output<URN>;
/**
* id is the provider-assigned unique ID for this managed resource. It is set during
* deployments and may be missing (undefined) during planning phases.
*/
public readonly id: Output<ID>;
/**
* Asynchronously creates a new `ProviderRef` for the given `ProviderResource`.
*/
public static async get(provider: ProviderResource): Promise<ProviderRef> {
const providerURN = await provider.urn.promise();
const providerID = await provider.id.promise() || unknownValue;
return new ProviderRef(provider.getPackage(), `${providerURN}::${providerID}`, provider.urn, provider.id);
}
/** @internal */
public static isInstance(obj: any): obj is ProviderRef {
return utils.isInstance<ProviderRef>(obj, "__providerRefInstance");
}
/** @internal */
constructor(pkg: string, value: string, urn: Output<URN>, id: Output<ID>) {
this.urn = urn;
this.id = id;
this.getPackage = () => pkg;
this.getValue = () => value;
}
}
/**
* Either a `ProviderResource` or a `ProviderRef`. In general, `ProviderRef`s are preferred over
* passing a `Provider`.
*/
export type ProviderResourceOrRef = ProviderResource | ProviderRef;
/**
* ComponentResource is a resource that aggregates one or more other child resources into a higher
* level abstraction. The component resource itself is a resource, but does not require custom CRUD
@ -916,7 +856,7 @@ function expandProviders(options: ComponentResourceOptions) {
function normalizeProviders(opts: ComponentResourceOptions) {
// If we have only 0-1 providers, then merge that back down to the .provider field.
const providers = <ProviderResourceOrRef[]>opts.providers;
const providers = <ProviderResource[]>opts.providers;
if (providers) {
if (providers.length === 0) {
delete opts.providers;

View file

@ -22,7 +22,7 @@ import { debuggablePromise } from "./debuggable";
import { deserializeProperties, serializeProperties, unknownValue } from "./rpc";
import { excessiveDebugOutput, getMonitor, rpcKeepAlive, SyncInvokes, tryGetSyncInvokes } from "./settings";
import { ProviderRef, Resource } from "../resource";
import { ProviderResource, Resource } from "../resource";
import * as utils from "../utils";
const gstruct = require("google-protobuf/google/protobuf/struct_pb.js");
@ -106,8 +106,8 @@ async function invokeAsync(tok: string, props: Inputs, opts: InvokeOptions): Pro
// Fetch the monitor and make an RPC request.
const monitor: any = getMonitor();
const providerRef = await getProviderRefAsync();
const req = createInvokeRequest(tok, serialized, providerRef, opts);
const provider = await ProviderResource.register(getProvider(tok, opts));
const req = createInvokeRequest(tok, serialized, provider, opts);
const resp: any = await debuggablePromise(new Promise((innerResolve, innerReject) =>
monitor.invoke(req, (err: grpc.StatusObject, innerResponse: any) => {
@ -138,20 +138,6 @@ async function invokeAsync(tok: string, props: Inputs, opts: InvokeOptions): Pro
finally {
done();
}
async function getProviderRefAsync() {
const provider = getProvider(tok, opts);
if (ProviderRef.isInstance(provider)) {
return provider;
}
else if (Resource.isInstance(provider)) {
return await ProviderRef.get(provider);
}
else {
return undefined;
}
}
}
function invokeSync(tok: string, props: any, opts: InvokeOptions, syncInvokes: SyncInvokes): Promise<any> {
@ -194,16 +180,16 @@ function invokeSync(tok: string, props: any, opts: InvokeOptions, syncInvokes: S
function getProviderRefSync() {
const provider = getProvider(tok, opts);
if (ProviderRef.isInstance(provider)) {
return provider;
}
else if (Resource.isInstance(provider)) {
// TODO(cyrusn): issue warning here that we are synchronously blocking an rpc call.
return utils.promiseResult(ProviderRef.get(provider));
}
else {
if (provider === undefined) {
return undefined;
}
if (provider.__registrationId === undefined) {
// TODO(cyrusn): issue warning here that we are synchronously blocking an rpc call.
utils.promiseResult(ProviderResource.register(provider));
}
return provider.__registrationId;
}
}
@ -217,9 +203,8 @@ function createLiftedPromise(value: any): Promise<any> {
return promise;
}
function createInvokeRequest(tok: string, serialized: any, providerRef: ProviderRef | undefined, opts: InvokeOptions) {
function createInvokeRequest(tok: string, serialized: any, provider: string | undefined, opts: InvokeOptions) {
const obj = gstruct.Struct.fromJavaScript(serialized);
const provider = providerRef ? providerRef.getValue() : undefined;
const req = new providerproto.InvokeRequest();
req.setTok(tok);

View file

@ -25,7 +25,7 @@ import {
CustomResource,
CustomResourceOptions,
ID,
ProviderRef,
ProviderResource,
Resource,
ResourceOptions,
URN,
@ -332,13 +332,7 @@ async function prepareResource(label: string, res: Resource, custom: boolean,
if (custom) {
const customOpts = <CustomResourceOptions>opts;
importID = customOpts.import;
if (ProviderRef.isInstance(customOpts.provider)) {
providerRef = customOpts.provider.getValue();
}
else if (Resource.isInstance(opts.provider)) {
providerRef = (await ProviderRef.get(opts.provider)).getValue();
}
providerRef = await ProviderResource.register(customOpts.provider)
}
// Collect the URNs for explicit/implicit dependencies for the engine so that it can understand