From eb756a322e4f3cefb0208dcb6b53329f9ea81de3 Mon Sep 17 00:00:00 2001 From: Kyle Pitzen Date: Mon, 31 Oct 2022 14:25:24 -0400 Subject: [PATCH] feat(sdk/nodejs): delegates alias computation to the engine --- ...as-computation-to-engine-for-node-sdk.yaml | 4 ++ sdk/nodejs/resource.ts | 8 +++- sdk/nodejs/runtime/resource.ts | 39 +++++++++++++++++-- sdk/nodejs/runtime/settings.ts | 9 +++++ .../072.large_alias_lineage_chains/index.js | 29 ++++++++++++++ sdk/nodejs/tests/runtime/langhost/run.spec.ts | 17 ++++---- 6 files changed, 91 insertions(+), 15 deletions(-) create mode 100644 changelog/pending/20221031--sdk-nodejs--delegates-alias-computation-to-engine-for-node-sdk.yaml diff --git a/changelog/pending/20221031--sdk-nodejs--delegates-alias-computation-to-engine-for-node-sdk.yaml b/changelog/pending/20221031--sdk-nodejs--delegates-alias-computation-to-engine-for-node-sdk.yaml new file mode 100644 index 000000000000..05d37f8cfae9 --- /dev/null +++ b/changelog/pending/20221031--sdk-nodejs--delegates-alias-computation-to-engine-for-node-sdk.yaml @@ -0,0 +1,4 @@ +changes: +- type: feat + scope: sdk/nodejs + description: Delegates alias computation to engine for Node SDK diff --git a/sdk/nodejs/resource.ts b/sdk/nodejs/resource.ts index 2534f2318e8e..01b71f4a8bde 100644 --- a/sdk/nodejs/resource.ts +++ b/sdk/nodejs/resource.ts @@ -15,7 +15,7 @@ import { ResourceError } from "./errors"; import { Input, Inputs, interpolate, Output, output } from "./output"; import { getResource, readResource, registerResource, registerResourceOutputs } from "./runtime/resource"; -import { getProject, getStack } from "./runtime/settings"; +import { getProject, getStack, monitorSupportsAliasSpecs } from "./runtime/settings"; import { getStackResource } from "./runtime/state"; import * as stacklib from "./runtime/stack"; import { unknownValue } from "./runtime/rpc"; @@ -334,7 +334,11 @@ export abstract class Resource { } // Update aliases to include the full set of aliases implied by the child and parent aliases. - opts.aliases = allAliases(opts.aliases || [], name, t, parent, parent.__name!); + monitorSupportsAliasSpecs().then(supportsAliasSpecs => { + if (!supportsAliasSpecs) { + opts.aliases = allAliases(opts.aliases || [], name, t, parent, parent.__name!); + } + }); this.__providers = parent.__providers; } diff --git a/sdk/nodejs/runtime/resource.ts b/sdk/nodejs/runtime/resource.ts index b94d2700bb91..0e110876d799 100644 --- a/sdk/nodejs/runtime/resource.ts +++ b/sdk/nodejs/runtime/resource.ts @@ -20,6 +20,7 @@ import * as utils from "../utils"; import { getAllResources, Input, Inputs, Output, output } from "../output"; import { ResolvedResource } from "../queryable"; import { + Alias, ComponentResource, ComponentResourceOptions, createUrn, @@ -52,6 +53,7 @@ import { getStack, isDryRun, isLegacyApplyEnabled, + monitorSupportsAliasSpecs, rpcKeepAlive, serialize, terminateRpcs, @@ -59,6 +61,7 @@ import { const gstruct = require("google-protobuf/google/protobuf/struct_pb.js"); const resproto = require("../proto/resource_pb.js"); +const aliasproto = require("../proto/alias_pb.js"); interface ResourceResolverOperation { // A resolver for a resource's URN. @@ -83,7 +86,7 @@ interface ResourceResolverOperation { // all be URNs of custom resources, not component resources. propertyToDirectDependencyURNs: Map>; // A list of aliases applied to this resource. - aliases: URN[]; + aliases: (Alias | URN)[]; // An ID to import, if any. import: ID | undefined; } @@ -272,6 +275,28 @@ export function readResource(res: Resource, parent: Resource | undefined, t: str }), label); } +function mapAliasesForRequest(aliases: (string | Alias)[] | undefined) { + if (aliases === undefined) { + return []; + } + + return aliases.map(a => { + const newAlias = new aliasproto.Alias(); + if (typeof a === "string") { + newAlias.setUrn(a); + } else { + const newAliasSpec = new aliasproto.Alias.Spec(); + newAliasSpec.setName(a.name); + newAliasSpec.setType(a.type); + newAliasSpec.setStack(a.stack); + newAliasSpec.setProject(a.project); + newAliasSpec.setParenturn(a.parent); + newAlias.setSpec(newAliasSpec); + } + return newAlias; + }); +} + /** * registerResource registers a new resource object with a given type t and name. It returns the auto-generated * URN and the ID that will resolve after the deployment has completed. All properties will be initialized to property @@ -310,7 +335,13 @@ export function registerResource(res: Resource, parent: Resource | undefined, t: req.setAcceptsecrets(true); req.setAcceptresources(!utils.disableResourceReferences); req.setAdditionalsecretoutputsList((opts).additionalSecretOutputs || []); - req.setAliasurnsList(resop.aliases); + monitorSupportsAliasSpecs().then(supportsAliasSpecs => { + if (supportsAliasSpecs) { + req.setAliasesList(mapAliasesForRequest(resop.aliases)); + } else { + req.setAliasurnsList(resop.aliases); + } + }); req.setImportid(resop.import || ""); req.setSupportspartialvalues(true); req.setRemote(remote); @@ -579,8 +610,8 @@ async function prepareResource(label: string, res: Resource, parent: Resource | // in the Resource constructor prior to calling `registerResource` - both adding new inherited aliases and // simplifying aliases down to URNs. const aliases = []; - const uniqueAliases = new Set(); - for (const alias of (res.__aliases || [])) { + const uniqueAliases = new Set(); + for (const alias of (opts.aliases || [])) { const aliasVal = await output(alias).promise(); if (!uniqueAliases.has(aliasVal)) { uniqueAliases.add(aliasVal); diff --git a/sdk/nodejs/runtime/settings.ts b/sdk/nodejs/runtime/settings.ts index 34e54412f934..e073e5e7e1c7 100644 --- a/sdk/nodejs/runtime/settings.ts +++ b/sdk/nodejs/runtime/settings.ts @@ -510,6 +510,15 @@ export async function monitorSupportsOutputValues(): Promise { return monitorSupportsFeature("outputValues"); } +/** + * monitorSupportsAliasSpecs returns a promise that when resolved tells you if the resource monitor we are + * connected to is able to support alias specs across its RPC interface. When it does, we marshal aliases + * in a special way. + */ +export async function monitorSupportsAliasSpecs(): Promise { + return monitorSupportsFeature("aliasSpecs"); +} + // sxsRandomIdentifier is a module level global that is transfered to process.env. // the goal is to detect side by side (sxs) pulumi/pulumi situations for inline programs // and fail fast. See https://github.com/pulumi/pulumi/issues/7333 for details. diff --git a/sdk/nodejs/tests/runtime/langhost/cases/072.large_alias_lineage_chains/index.js b/sdk/nodejs/tests/runtime/langhost/cases/072.large_alias_lineage_chains/index.js index 23cdb785836a..ac595b163a58 100644 --- a/sdk/nodejs/tests/runtime/langhost/cases/072.large_alias_lineage_chains/index.js +++ b/sdk/nodejs/tests/runtime/langhost/cases/072.large_alias_lineage_chains/index.js @@ -18,10 +18,39 @@ class MyResource extends pulumi.CustomResource { } } +class MyOtherResource extends pulumi.CustomResource { + constructor(name, aliases, parent) { + super( + "test:index:MyOtherResource", + name, + {}, + { + aliases: aliases, + parent + } + ); + } +} + const resource1Aliases = Array.from(new Array(1000).keys()).map(key => `my-alias-name-${key}`); const resource1 = new MyResource("testResource1", resource1Aliases); resource1.__aliases.map(alias => alias.apply(aliasName => assert(resource1Aliases.includes(aliasName)))); +assert.equal(resource1.__aliases.length, 1000); const resource2Aliases = Array.from(new Array(1000).keys()).map(key => `my-other-alias-name-${key}`); const resource2 = new MyResource("testResource2", resource2Aliases, resource1) resource2.__aliases.map(alias => alias.apply(aliasName => assert(resource2Aliases.includes(aliasName)))); +assert.equal(resource2.__aliases.length, 1000); + +const resource3Aliases = Array.from(new Array(1000).keys()).map(key => { + return { + name: `my-alias-${key}`, + stack: "my-stack", + project: "my-project", + type: "test:index:MyOtherResource", + } +}); +const resource3 = new MyOtherResource("testResource2", resource3Aliases, resource2) +assert.equal(resource3.__aliases.length, 1000); +// We want to ensure that the parent's type is included in computed aliases from the engine +resource3.__aliases[0].apply(aliasName => assert(aliasName.includes("test:index:MyResource"))); diff --git a/sdk/nodejs/tests/runtime/langhost/run.spec.ts b/sdk/nodejs/tests/runtime/langhost/run.spec.ts index 72013bfade05..4d17679f0dba 100644 --- a/sdk/nodejs/tests/runtime/langhost/run.spec.ts +++ b/sdk/nodejs/tests/runtime/langhost/run.spec.ts @@ -1249,14 +1249,13 @@ describe("rpc", () => { return { urn: makeUrn(t, name), id: undefined, props: undefined }; }, }, - /** Skipping this test case as it requires limiting the alias multiplication which occurs */ - // "large_alias_lineage_chains": { - // program: path.join(base, "072.large_alias_lineage_chains"), - // expectResourceCount: 1, - // registerResource: (ctx: any, dryrun: boolean, t: string, name: string, res: any, ...args: any) => { - // return { urn: makeUrn(t, name), id: undefined, props: undefined }; - // }, - // } + "large_alias_lineage_chains": { + program: path.join(base, "072.large_alias_lineage_chains"), + expectResourceCount: 3, + registerResource: (ctx: any, dryrun: boolean, t: string, name: string, res: any, ...args: any) => { + return { urn: makeUrn(t, name), id: undefined, props: undefined }; + }, + }, }; for (const casename of Object.keys(cases)) { @@ -1418,7 +1417,7 @@ describe("rpc", () => { // SupportsFeature callback (call: any, callback: any) => { const resp = new resproto.SupportsFeatureResponse(); - resp.setHassupport(false); + resp.setHassupport(true); callback(undefined, resp); }, );