github.com/pulumi/pulumi/sdk/v3@v3.108.1/nodejs/runtime/resource.ts (about)

     1  // Copyright 2016-2021, Pulumi Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  import * as grpc from "@grpc/grpc-js";
    16  import * as query from "@pulumi/query";
    17  import * as log from "../log";
    18  import * as utils from "../utils";
    19  
    20  import { getAllResources, Input, Inputs, Output, output } from "../output";
    21  import { ResolvedResource } from "../queryable";
    22  import {
    23      Alias,
    24      allAliases,
    25      ComponentResource,
    26      ComponentResourceOptions,
    27      createUrn,
    28      CustomResource,
    29      CustomResourceOptions,
    30      expandProviders,
    31      ID,
    32      ProviderResource,
    33      Resource,
    34      ResourceOptions,
    35      URN,
    36  } from "../resource";
    37  import { debuggablePromise, debugPromiseLeaks } from "./debuggable";
    38  import { invoke } from "./invoke";
    39  import { getStore } from "./state";
    40  
    41  import { isGrpcError } from "../errors";
    42  import {
    43      deserializeProperties,
    44      deserializeProperty,
    45      OutputResolvers,
    46      resolveProperties,
    47      serializeProperties,
    48      serializeProperty,
    49      serializeResourceProperties,
    50      suppressUnhandledGrpcRejections,
    51      transferProperties,
    52  } from "./rpc";
    53  import {
    54      excessiveDebugOutput,
    55      getMonitor,
    56      getStack,
    57      isDryRun,
    58      isLegacyApplyEnabled,
    59      rpcKeepAlive,
    60      serialize,
    61      terminateRpcs,
    62  } from "./settings";
    63  
    64  import * as gempty from "google-protobuf/google/protobuf/empty_pb";
    65  import * as gstruct from "google-protobuf/google/protobuf/struct_pb";
    66  import * as aliasproto from "../proto/alias_pb";
    67  import * as provproto from "../proto/provider_pb";
    68  import * as resproto from "../proto/resource_pb";
    69  import * as sourceproto from "../proto/source_pb";
    70  
    71  export interface SourcePosition {
    72      uri: string;
    73      line: number;
    74      column: number;
    75  }
    76  
    77  function marshalSourcePosition(sourcePosition?: SourcePosition) {
    78      if (sourcePosition === undefined) {
    79          return undefined;
    80      }
    81      const pos = new sourceproto.SourcePosition();
    82      pos.setUri(sourcePosition.uri);
    83      pos.setLine(sourcePosition.line);
    84      pos.setColumn(sourcePosition.column);
    85      return pos;
    86  }
    87  
    88  interface ResourceResolverOperation {
    89      // A resolver for a resource's URN.
    90      resolveURN: (urn: URN, err?: Error) => void;
    91      // A resolver for a resource's ID (for custom resources only).
    92      resolveID: ((v: ID, performApply: boolean, err?: Error) => void) | undefined;
    93      // A collection of resolvers for a resource's properties.
    94      resolvers: OutputResolvers;
    95      // A parent URN, fully resolved, if any.
    96      parentURN: URN | undefined;
    97      // A provider reference, fully resolved, if any.
    98      providerRef: string | undefined;
    99      // A map of provider references, fully resolved, if any.
   100      providerRefs: Map<string, string>;
   101      // All serialized properties, fully awaited, serialized, and ready to go.
   102      serializedProps: Record<string, any>;
   103      // A set of URNs that this resource is directly dependent upon.  These will all be URNs of
   104      // custom resources, not component resources.
   105      allDirectDependencyURNs: Set<URN>;
   106      // Set of URNs that this resource is directly dependent upon, keyed by the property that causes
   107      // the dependency.  All urns in this map must exist in [allDirectDependencyURNs].  These will
   108      // all be URNs of custom resources, not component resources.
   109      propertyToDirectDependencyURNs: Map<string, Set<URN>>;
   110      // A list of aliases applied to this resource.
   111      aliases: (Alias | URN)[];
   112      // An ID to import, if any.
   113      import: ID | undefined;
   114      // Any important feature support from the monitor.
   115      monitorSupportsStructuredAliases: boolean;
   116      // If set, the providers Delete method will not be called for this resource
   117      // if specified is being deleted as well.
   118      deletedWithURN: URN | undefined;
   119  }
   120  
   121  /**
   122   * Get an existing resource's state from the engine.
   123   */
   124  export function getResource(
   125      res: Resource,
   126      parent: Resource | undefined,
   127      props: Inputs,
   128      custom: boolean,
   129      urn: string,
   130  ): void {
   131      // Extract the resource type from the URN.
   132      const urnParts = urn.split("::");
   133      const qualifiedType = urnParts[2];
   134      const urnName = urnParts[3];
   135      const type = qualifiedType.split("$").pop()!;
   136  
   137      const label = `resource:urn=${urn}`;
   138      log.debug(`Getting resource: urn=${urn}`);
   139  
   140      const monitor = getMonitor();
   141      const resopAsync = prepareResource(label, res, parent, custom, false, props, {});
   142  
   143      const preallocError = new Error();
   144      debuggablePromise(
   145          resopAsync.then(async (resop) => {
   146              const inputs = await serializeProperties(label, { urn });
   147  
   148              const req = new resproto.ResourceInvokeRequest();
   149              req.setTok("pulumi:pulumi:getResource");
   150              req.setArgs(gstruct.Struct.fromJavaScript(inputs));
   151              req.setProvider("");
   152              req.setVersion("");
   153              req.setAcceptresources(!utils.disableResourceReferences);
   154  
   155              // Now run the operation, serializing the invocation if necessary.
   156              const opLabel = `monitor.getResource(${label})`;
   157              runAsyncResourceOp(opLabel, async () => {
   158                  let resp: any = {};
   159                  let err: Error | undefined;
   160                  try {
   161                      if (monitor) {
   162                          resp = await debuggablePromise(
   163                              new Promise((resolve, reject) =>
   164                                  monitor.invoke(
   165                                      req,
   166                                      (
   167                                          rpcError: grpc.ServiceError | null,
   168                                          innerResponse: provproto.InvokeResponse | undefined,
   169                                      ) => {
   170                                          log.debug(
   171                                              `getResource Invoke RPC finished: err: ${rpcError}, resp: ${innerResponse}`,
   172                                          );
   173                                          if (rpcError) {
   174                                              if (
   175                                                  rpcError.code === grpc.status.UNAVAILABLE ||
   176                                                  rpcError.code === grpc.status.CANCELLED
   177                                              ) {
   178                                                  err = rpcError;
   179                                                  terminateRpcs();
   180                                                  rpcError.message = "Resource monitor is terminating";
   181                                                  (<any>preallocError).code = rpcError.code;
   182                                              }
   183  
   184                                              preallocError.message = `failed to get resource:urn=${urn}: ${rpcError.message}`;
   185                                              reject(new Error(rpcError.details));
   186                                          } else {
   187                                              resolve(innerResponse);
   188                                          }
   189                                      },
   190                                  ),
   191                              ),
   192                              opLabel,
   193                          );
   194  
   195                          // If the invoke failed, raise an error
   196                          const failures = resp.getFailuresList();
   197                          if (failures?.length) {
   198                              let reasons = "";
   199                              for (let i = 0; i < failures.length; i++) {
   200                                  if (reasons !== "") {
   201                                      reasons += "; ";
   202                                  }
   203                                  reasons += `${failures[i].getReason()} (${failures[i].getProperty()})`;
   204                              }
   205                              throw new Error(`getResource Invoke failed: ${reasons}`);
   206                          }
   207  
   208                          // Otherwise, return the response.
   209                          const m = resp.getReturn().getFieldsMap();
   210                          resp = {
   211                              urn: m.get("urn").toJavaScript(),
   212                              id: m.get("id").toJavaScript() || undefined,
   213                              state: m.get("state").getStructValue(),
   214                          };
   215                      }
   216                  } catch (e) {
   217                      err = e;
   218                      resp = {
   219                          urn: "",
   220                          id: undefined,
   221                          state: undefined,
   222                      };
   223                  }
   224  
   225                  resop.resolveURN(resp.urn, err);
   226  
   227                  // Note: 'id || undefined' is intentional.  We intentionally collapse falsy values to
   228                  // undefined so that later parts of our system don't have to deal with values like 'null'.
   229                  if (resop.resolveID) {
   230                      const id = resp.id || undefined;
   231                      resop.resolveID(id, id !== undefined, err);
   232                  }
   233  
   234                  await resolveOutputs(res, type, urnName, props, resp.state, {}, resop.resolvers, err);
   235              });
   236          }),
   237          label,
   238      );
   239  }
   240  
   241  /**
   242   * Reads an existing custom resource's state from the resource monitor.  Note that resources read in this way
   243   * will not be part of the resulting stack's state, as they are presumed to belong to another.
   244   */
   245  export function readResource(
   246      res: Resource,
   247      parent: Resource | undefined,
   248      t: string,
   249      name: string,
   250      props: Inputs,
   251      opts: ResourceOptions,
   252      sourcePosition?: SourcePosition,
   253  ): void {
   254      if (!opts.id) {
   255          throw new Error("Cannot read resource whose options are lacking an ID value");
   256      }
   257      const id: Promise<Input<ID>> = output(opts.id).promise(true);
   258  
   259      const label = `resource:${name}[${t}]#...`;
   260      log.debug(`Reading resource: t=${t}, name=${name}`);
   261  
   262      const monitor = getMonitor();
   263      const resopAsync = prepareResource(label, res, parent, true, false, props, opts);
   264  
   265      const preallocError = new Error();
   266      debuggablePromise(
   267          resopAsync.then(async (resop) => {
   268              const resolvedID = await serializeProperty(label, await id, new Set(), { keepOutputValues: false });
   269              log.debug(
   270                  `ReadResource RPC prepared: id=${resolvedID}, t=${t}, name=${name}` +
   271                      (excessiveDebugOutput ? `, obj=${JSON.stringify(resop.serializedProps)}` : ``),
   272              );
   273  
   274              // Create a resource request and do the RPC.
   275              const req = new resproto.ReadResourceRequest();
   276              req.setType(t);
   277              req.setName(name);
   278              req.setId(resolvedID);
   279              req.setParent(resop.parentURN || "");
   280              req.setProvider(resop.providerRef || "");
   281              req.setProperties(gstruct.Struct.fromJavaScript(resop.serializedProps));
   282              req.setDependenciesList(Array.from(resop.allDirectDependencyURNs));
   283              req.setVersion(opts.version || "");
   284              req.setPlugindownloadurl(opts.pluginDownloadURL || "");
   285              req.setAcceptsecrets(true);
   286              req.setAcceptresources(!utils.disableResourceReferences);
   287              req.setAdditionalsecretoutputsList((<any>opts).additionalSecretOutputs || []);
   288              req.setSourceposition(marshalSourcePosition(sourcePosition));
   289  
   290              // Now run the operation, serializing the invocation if necessary.
   291              const opLabel = `monitor.readResource(${label})`;
   292              runAsyncResourceOp(opLabel, async () => {
   293                  let resp: any = {};
   294                  let err: Error | undefined;
   295                  try {
   296                      if (monitor) {
   297                          // If we're attached to the engine, make an RPC call and wait for it to resolve.
   298                          resp = await debuggablePromise(
   299                              new Promise((resolve, reject) =>
   300                                  monitor.readResource(
   301                                      req,
   302                                      (
   303                                          rpcError: grpc.ServiceError | null,
   304                                          innerResponse: resproto.ReadResourceResponse | undefined,
   305                                      ) => {
   306                                          log.debug(
   307                                              `ReadResource RPC finished: ${label}; err: ${rpcError}, resp: ${innerResponse}`,
   308                                          );
   309                                          if (rpcError) {
   310                                              if (
   311                                                  rpcError.code === grpc.status.UNAVAILABLE ||
   312                                                  rpcError.code === grpc.status.CANCELLED
   313                                              ) {
   314                                                  err = rpcError;
   315                                                  terminateRpcs();
   316                                                  rpcError.message = "Resource monitor is terminating";
   317                                                  (<any>preallocError).code = rpcError.code;
   318                                              }
   319  
   320                                              preallocError.message = `failed to read resource #${resolvedID} '${name}' [${t}]: ${rpcError.message}`;
   321                                              reject(preallocError);
   322                                          } else {
   323                                              resolve(innerResponse);
   324                                          }
   325                                      },
   326                                  ),
   327                              ),
   328                              opLabel,
   329                          );
   330                      } else {
   331                          // If we aren't attached to the engine, in test mode, mock up a fake response for testing purposes.
   332                          const mockurn = await createUrn(req.getName(), req.getType(), req.getParent()).promise();
   333                          resp = {
   334                              getUrn: () => mockurn,
   335                              getProperties: () => req.getProperties(),
   336                          };
   337                      }
   338                  } catch (e) {
   339                      err = e;
   340                      resp = {
   341                          getUrn: () => "",
   342                          getProperties: () => undefined,
   343                      };
   344                  }
   345  
   346                  // Now resolve everything: the URN, the ID (supplied as input), and the output properties.
   347                  resop.resolveURN(resp.getUrn(), err);
   348                  resop.resolveID!(resolvedID, resolvedID !== undefined, err);
   349                  await resolveOutputs(res, t, name, props, resp.getProperties(), {}, resop.resolvers, err);
   350              });
   351          }),
   352          label,
   353      );
   354  }
   355  
   356  function getParentURN(parent?: Resource | Input<string>) {
   357      if (Resource.isInstance(parent)) {
   358          return parent.urn;
   359      }
   360      return output(parent);
   361  }
   362  
   363  function mapAliasesForRequest(aliases: (URN | Alias)[] | undefined, parentURN?: URN) {
   364      if (aliases === undefined) {
   365          return [];
   366      }
   367  
   368      return Promise.all(
   369          aliases.map(async (a) => {
   370              const newAlias = new aliasproto.Alias();
   371              if (typeof a === "string") {
   372                  newAlias.setUrn(a);
   373              } else {
   374                  const newAliasSpec = new aliasproto.Alias.Spec();
   375                  const name = a.name === undefined ? undefined : await output(a.name).promise();
   376                  const type = a.type === undefined ? undefined : await output(a.type).promise();
   377                  const stack = a.stack === undefined ? undefined : await output(a.stack).promise();
   378                  const project = a.project === undefined ? undefined : await output(a.project).promise();
   379  
   380                  newAliasSpec.setName(name || "");
   381                  newAliasSpec.setType(type || "");
   382                  newAliasSpec.setStack(stack || "");
   383                  newAliasSpec.setProject(project || "");
   384                  if (a.hasOwnProperty("parent")) {
   385                      if (a.parent === undefined) {
   386                          newAliasSpec.setNoparent(true);
   387                      } else {
   388                          const aliasParentUrn = getParentURN(a.parent);
   389                          const urn = await aliasParentUrn.promise();
   390                          if (urn !== undefined) {
   391                              newAliasSpec.setParenturn(urn);
   392                          }
   393                      }
   394                  } else if (parentURN) {
   395                      // If a parent isn't specified for the alias and the resource has a parent,
   396                      // pass along the resource's parent in the alias spec.
   397                      // It shouldn't be necessary to do this because the engine should fill-in the
   398                      // resource's parent if one wasn't specified for the alias.
   399                      // However, some older versions of the CLI don't do this correctly, and this
   400                      // SDK has always passed along the parent in this way, so we continue doing it
   401                      // to maintain compatibility with these versions of the CLI.
   402                      newAliasSpec.setParenturn(parentURN);
   403                  }
   404                  newAlias.setSpec(newAliasSpec);
   405              }
   406              return newAlias;
   407          }),
   408      );
   409  }
   410  
   411  /**
   412   * registerResource registers a new resource object with a given type t and name.  It returns the auto-generated
   413   * URN and the ID that will resolve after the deployment has completed.  All properties will be initialized to property
   414   * objects that the registration operation will resolve at the right time (or remain unresolved for deployments).
   415   */
   416  export function registerResource(
   417      res: Resource,
   418      parent: Resource | undefined,
   419      t: string,
   420      name: string,
   421      custom: boolean,
   422      remote: boolean,
   423      newDependency: (urn: URN) => Resource,
   424      props: Inputs,
   425      opts: ResourceOptions,
   426      sourcePosition?: SourcePosition,
   427  ): void {
   428      const label = `resource:${name}[${t}]`;
   429      log.debug(`Registering resource: t=${t}, name=${name}, custom=${custom}, remote=${remote}`);
   430  
   431      const monitor = getMonitor();
   432      const resopAsync = prepareResource(label, res, parent, custom, remote, props, opts, t, name);
   433  
   434      // In order to present a useful stack trace if an error does occur, we preallocate potential
   435      // errors here. V8 captures a stack trace at the moment an Error is created and this stack
   436      // trace will lead directly to user code. Throwing in `runAsyncResourceOp` results in an Error
   437      // with a non-useful stack trace.
   438      const preallocError = new Error();
   439      debuggablePromise(
   440          resopAsync.then(async (resop) => {
   441              log.debug(
   442                  `RegisterResource RPC prepared: t=${t}, name=${name}` +
   443                      (excessiveDebugOutput ? `, obj=${JSON.stringify(resop.serializedProps)}` : ``),
   444              );
   445  
   446              const req = new resproto.RegisterResourceRequest();
   447              req.setType(t);
   448              req.setName(name);
   449              req.setParent(resop.parentURN || "");
   450              req.setCustom(custom);
   451              req.setObject(gstruct.Struct.fromJavaScript(resop.serializedProps));
   452              req.setProtect(opts.protect || false);
   453              req.setProvider(resop.providerRef || "");
   454              req.setDependenciesList(Array.from(resop.allDirectDependencyURNs));
   455              req.setDeletebeforereplace((<any>opts).deleteBeforeReplace || false);
   456              req.setDeletebeforereplacedefined((<any>opts).deleteBeforeReplace !== undefined);
   457              req.setIgnorechangesList(opts.ignoreChanges || []);
   458              req.setVersion(opts.version || "");
   459              req.setAcceptsecrets(true);
   460              req.setAcceptresources(!utils.disableResourceReferences);
   461              req.setAdditionalsecretoutputsList((<any>opts).additionalSecretOutputs || []);
   462              if (resop.monitorSupportsStructuredAliases) {
   463                  const aliasesList = await mapAliasesForRequest(resop.aliases, resop.parentURN);
   464                  req.setAliasesList(aliasesList);
   465              } else {
   466                  const urns = new Array<string>();
   467                  resop.aliases.forEach((v) => {
   468                      if (typeof v === "string") {
   469                          urns.push(v);
   470                      }
   471                  });
   472                  req.setAliasurnsList(urns);
   473              }
   474              req.setImportid(resop.import || "");
   475              req.setSupportspartialvalues(true);
   476              req.setRemote(remote);
   477              req.setReplaceonchangesList(opts.replaceOnChanges || []);
   478              req.setPlugindownloadurl(opts.pluginDownloadURL || "");
   479              req.setRetainondelete(opts.retainOnDelete || false);
   480              req.setDeletedwith(resop.deletedWithURN || "");
   481              req.setAliasspecs(true);
   482              req.setSourceposition(marshalSourcePosition(sourcePosition));
   483  
   484              if (resop.deletedWithURN && !getStore().supportsDeletedWith) {
   485                  throw new Error(
   486                      "The Pulumi CLI does not support the DeletedWith option. Please update the Pulumi CLI.",
   487                  );
   488              }
   489  
   490              const customTimeouts = new resproto.RegisterResourceRequest.CustomTimeouts();
   491              if (opts.customTimeouts != null) {
   492                  customTimeouts.setCreate(opts.customTimeouts.create || "");
   493                  customTimeouts.setUpdate(opts.customTimeouts.update || "");
   494                  customTimeouts.setDelete(opts.customTimeouts.delete || "");
   495              }
   496              req.setCustomtimeouts(customTimeouts);
   497  
   498              const propertyDependencies = req.getPropertydependenciesMap();
   499              for (const [key, resourceURNs] of resop.propertyToDirectDependencyURNs) {
   500                  const deps = new resproto.RegisterResourceRequest.PropertyDependencies();
   501                  deps.setUrnsList(Array.from(resourceURNs));
   502                  propertyDependencies.set(key, deps);
   503              }
   504  
   505              const providerRefs = req.getProvidersMap();
   506              for (const [key, ref] of resop.providerRefs) {
   507                  providerRefs.set(key, ref);
   508              }
   509  
   510              // Now run the operation, serializing the invocation if necessary.
   511              const opLabel = `monitor.registerResource(${label})`;
   512              runAsyncResourceOp(opLabel, async () => {
   513                  let resp: any = {};
   514                  let err: Error | undefined;
   515                  try {
   516                      if (monitor) {
   517                          // If we're running with an attachment to the engine, perform the operation.
   518                          resp = await debuggablePromise(
   519                              new Promise((resolve, reject) =>
   520                                  monitor.registerResource(
   521                                      req,
   522                                      (
   523                                          rpcErr: grpc.ServiceError | null,
   524                                          innerResponse: resproto.RegisterResourceResponse | undefined,
   525                                      ) => {
   526                                          if (rpcErr) {
   527                                              err = rpcErr;
   528                                              // If the monitor is unavailable, it is in the process of shutting down or has already
   529                                              // shut down. Don't emit an error and don't do any more RPCs, just exit.
   530                                              if (
   531                                                  rpcErr.code === grpc.status.UNAVAILABLE ||
   532                                                  rpcErr.code === grpc.status.CANCELLED
   533                                              ) {
   534                                                  // Re-emit the message
   535                                                  terminateRpcs();
   536                                                  rpcErr.message = "Resource monitor is terminating";
   537                                                  (<any>preallocError).code = rpcErr.code;
   538                                              }
   539  
   540                                              // Node lets us hack the message as long as we do it before accessing the `stack` property.
   541                                              log.debug(
   542                                                  `RegisterResource RPC finished: ${label}; err: ${rpcErr}, resp: ${innerResponse}`,
   543                                              );
   544                                              preallocError.message = `failed to register new resource ${name} [${t}]: ${rpcErr.message}`;
   545                                              reject(preallocError);
   546                                          } else {
   547                                              log.debug(
   548                                                  `RegisterResource RPC finished: ${label}; err: ${rpcErr}, resp: ${innerResponse}`,
   549                                              );
   550                                              resolve(innerResponse);
   551                                          }
   552                                      },
   553                                  ),
   554                              ),
   555                              opLabel,
   556                          );
   557                      } else {
   558                          // If we aren't attached to the engine, in test mode, mock up a fake response for testing purposes.
   559                          const mockurn = await createUrn(req.getName(), req.getType(), req.getParent()).promise();
   560                          resp = {
   561                              getUrn: () => mockurn,
   562                              getId: () => undefined,
   563                              getObject: () => req.getObject(),
   564                              getPropertydependenciesMap: () => undefined,
   565                          };
   566                      }
   567                  } catch (e) {
   568                      err = e;
   569                      resp = {
   570                          getUrn: () => "",
   571                          getId: () => undefined,
   572                          getObject: () => req.getObject(),
   573                          getPropertydependenciesMap: () => undefined,
   574                      };
   575                  }
   576  
   577                  resop.resolveURN(resp.getUrn(), err);
   578  
   579                  // Note: 'id || undefined' is intentional.  We intentionally collapse falsy values to
   580                  // undefined so that later parts of our system don't have to deal with values like 'null'.
   581                  if (resop.resolveID) {
   582                      const id = resp.getId() || undefined;
   583                      resop.resolveID(id, id !== undefined, err);
   584                  }
   585  
   586                  const deps: Record<string, Resource[]> = {};
   587                  const rpcDeps = resp.getPropertydependenciesMap();
   588                  if (rpcDeps) {
   589                      for (const [k, propertyDeps] of resp.getPropertydependenciesMap().entries()) {
   590                          const urns = <URN[]>propertyDeps.getUrnsList();
   591                          deps[k] = urns.map((urn) => newDependency(urn));
   592                      }
   593                  }
   594  
   595                  // Now resolve the output properties.
   596                  await resolveOutputs(res, t, name, props, resp.getObject(), deps, resop.resolvers, err);
   597              });
   598          }),
   599          label,
   600      );
   601  }
   602  
   603  /** @internal
   604   * Prepares for an RPC that will manufacture a resource, and hence deals with input and output
   605   * properties.
   606   */
   607  export async function prepareResource(
   608      label: string,
   609      res: Resource,
   610      parent: Resource | undefined,
   611      custom: boolean,
   612      remote: boolean,
   613      props: Inputs,
   614      opts: ResourceOptions,
   615      type?: string,
   616      name?: string,
   617  ): Promise<ResourceResolverOperation> {
   618      // add an entry to the rpc queue while we prepare the request.
   619      // automation api inline programs that don't have stack exports can exit quickly. If we don't do this,
   620      // sometimes they will exit right after `prepareResource` is called as a part of register resource, but before the
   621      // .then() that adds to the queue via `runAsyncResourceOp`.
   622      const done: () => void = rpcKeepAlive();
   623  
   624      try {
   625          // Simply initialize the URN property and get prepared to resolve it later on.
   626          // Note: a resource urn will always get a value, and thus the output property
   627          // for it can always run .apply calls.
   628          let resolveURN: (urn: URN, err?: Error) => void;
   629          {
   630              let resolveValue: (urn: URN) => void;
   631              let rejectValue: (err: Error) => void;
   632              let resolveIsKnown: (isKnown: boolean) => void;
   633              let rejectIsKnown: (err: Error) => void;
   634              (res as any).urn = new Output(
   635                  res,
   636                  debuggablePromise(
   637                      new Promise<URN>((resolve, reject) => {
   638                          resolveValue = resolve;
   639                          rejectValue = reject;
   640                      }),
   641                      `resolveURN(${label})`,
   642                  ),
   643                  debuggablePromise(
   644                      new Promise<boolean>((resolve, reject) => {
   645                          resolveIsKnown = resolve;
   646                          rejectIsKnown = reject;
   647                      }),
   648                      `resolveURNIsKnown(${label})`,
   649                  ),
   650                  /*isSecret:*/ Promise.resolve(false),
   651                  Promise.resolve(res),
   652              );
   653  
   654              resolveURN = (v, err) => {
   655                  if (err) {
   656                      if (isGrpcError(err)) {
   657                          if (debugPromiseLeaks) {
   658                              console.error("info: skipped rejection in resolveURN");
   659                          }
   660                          return;
   661                      }
   662                      rejectValue(err);
   663                      rejectIsKnown(err);
   664                  } else {
   665                      resolveValue(v);
   666                      resolveIsKnown(true);
   667                  }
   668              };
   669          }
   670  
   671          // If a custom resource, make room for the ID property.
   672          let resolveID: ((v: any, performApply: boolean, err?: Error) => void) | undefined;
   673          if (custom) {
   674              let resolveValue: (v: ID) => void;
   675              let rejectValue: (err: Error) => void;
   676              let resolveIsKnown: (v: boolean) => void;
   677              let rejectIsKnown: (err: Error) => void;
   678  
   679              (res as any).id = new Output(
   680                  res,
   681                  debuggablePromise(
   682                      new Promise<ID>((resolve, reject) => {
   683                          resolveValue = resolve;
   684                          rejectValue = reject;
   685                      }),
   686                      `resolveID(${label})`,
   687                  ),
   688                  debuggablePromise(
   689                      new Promise<boolean>((resolve, reject) => {
   690                          resolveIsKnown = resolve;
   691                          rejectIsKnown = reject;
   692                      }),
   693                      `resolveIDIsKnown(${label})`,
   694                  ),
   695                  Promise.resolve(false),
   696                  Promise.resolve(res),
   697              );
   698  
   699              resolveID = (v, isKnown, err) => {
   700                  if (err) {
   701                      if (isGrpcError(err)) {
   702                          if (debugPromiseLeaks) {
   703                              console.error("info: skipped rejection in resolveID");
   704                          }
   705                          return;
   706                      }
   707                      rejectValue(err);
   708                      rejectIsKnown(err);
   709                  } else {
   710                      resolveValue(v);
   711                      resolveIsKnown(isKnown);
   712                  }
   713              };
   714          }
   715  
   716          // Now "transfer" all input properties into unresolved Promises on res.  This way,
   717          // this resource will look like it has all its output properties to anyone it is
   718          // passed to.  However, those promises won't actually resolve until the registerResource
   719          // RPC returns
   720          const resolvers = transferProperties(res, label, props);
   721  
   722          /** IMPORTANT!  We should never await prior to this line, otherwise the Resource will be partly uninitialized. */
   723  
   724          // Before we can proceed, all our dependencies must be finished.
   725          const explicitDirectDependencies = new Set(await gatherExplicitDependencies(opts.dependsOn));
   726  
   727          // Serialize out all our props to their final values.  In doing so, we'll also collect all
   728          // the Resources pointed to by any Dependency objects we encounter, adding them to 'propertyDependencies'.
   729          const [serializedProps, propertyToDirectDependencies] = await serializeResourceProperties(label, props, {
   730              // To initially scope the use of this new feature, we only keep output values when
   731              // remote is true (for multi-lang components).
   732              keepOutputValues: remote,
   733          });
   734  
   735          // Wait for the parent to complete.
   736          // If no parent was provided, parent to the root resource.
   737          const parentURN = parent ? await parent.urn.promise() : undefined;
   738  
   739          let importID: ID | undefined;
   740          if (custom) {
   741              const customOpts = <CustomResourceOptions>opts;
   742              importID = customOpts.import;
   743          }
   744  
   745          let providerRef: string | undefined;
   746          let sendProvider = custom;
   747          if (remote && opts.provider) {
   748              // If it's a remote component and a provider was specified, only
   749              // send the provider in the request if the provider's package is
   750              // the same as the component's package. Otherwise, don't send it
   751              // because the user specified `provider: someProvider` as shorthand
   752              // for `providers: [someProvider]`.
   753              const pkg = pkgFromType(type!);
   754              if (pkg && pkg === opts.provider.getPackage()) {
   755                  sendProvider = true;
   756              }
   757          }
   758          if (sendProvider) {
   759              providerRef = await ProviderResource.register(opts.provider);
   760          }
   761  
   762          const providerRefs: Map<string, string> = new Map<string, string>();
   763          if (remote || !custom) {
   764              const componentOpts = <ComponentResourceOptions>opts;
   765              expandProviders(componentOpts);
   766              // the <ProviderResource[]> casts are safe because expandProviders
   767              // /always/ leaves providers as an array.
   768              if (componentOpts.provider !== undefined) {
   769                  if (componentOpts.providers === undefined) {
   770                      // We still want to do the promotion, so we define providers
   771                      componentOpts.providers = [componentOpts.provider];
   772                  } else if ((<ProviderResource[]>componentOpts.providers)?.indexOf(componentOpts.provider) !== -1) {
   773                      const pkg = componentOpts.provider.getPackage();
   774                      const message = `There is a conflit between the 'provider' field (${pkg}) and a member of the 'providers' map'. `;
   775                      const deprecationd =
   776                          "This will become an error in a future version. See https://github.com/pulumi/pulumi/issues/8799 for more details";
   777                      log.warn(message + deprecationd);
   778                  } else {
   779                      (<ProviderResource[]>componentOpts.providers).push(componentOpts.provider);
   780                  }
   781              }
   782              if (componentOpts.providers) {
   783                  for (const provider of componentOpts.providers as ProviderResource[]) {
   784                      const pref = await ProviderResource.register(provider);
   785                      if (pref) {
   786                          providerRefs.set(provider.getPackage(), pref);
   787                      }
   788                  }
   789              }
   790          }
   791  
   792          // Collect the URNs for explicit/implicit dependencies for the engine so that it can understand
   793          // the dependency graph and optimize operations accordingly.
   794  
   795          // The list of all dependencies (implicit or explicit).
   796          const allDirectDependencies = new Set<Resource>(explicitDirectDependencies);
   797  
   798          const exclude = new Set<Resource>([res]);
   799          const allDirectDependencyURNs = await getAllTransitivelyReferencedResourceURNs(
   800              explicitDirectDependencies,
   801              exclude,
   802          );
   803          const propertyToDirectDependencyURNs = new Map<string, Set<URN>>();
   804  
   805          for (const [propertyName, directDependencies] of propertyToDirectDependencies) {
   806              addAll(allDirectDependencies, directDependencies);
   807  
   808              const urns = await getAllTransitivelyReferencedResourceURNs(directDependencies, exclude);
   809              addAll(allDirectDependencyURNs, urns);
   810              propertyToDirectDependencyURNs.set(propertyName, urns);
   811          }
   812  
   813          const monitorSupportsStructuredAliases = getStore().supportsAliasSpecs;
   814          let computedAliases;
   815          if (!monitorSupportsStructuredAliases && parent) {
   816              computedAliases = allAliases(opts.aliases || [], name!, type!, parent, parent.__name!);
   817          } else {
   818              computedAliases = opts.aliases || [];
   819          }
   820  
   821          // Wait for all aliases.
   822          const aliases = [];
   823          const uniqueAliases = new Set<Alias | URN>();
   824          for (const alias of computedAliases || []) {
   825              const aliasVal = await output(alias).promise();
   826              if (!uniqueAliases.has(aliasVal)) {
   827                  uniqueAliases.add(aliasVal);
   828                  aliases.push(aliasVal);
   829              }
   830          }
   831  
   832          const deletedWithURN = opts?.deletedWith ? await opts.deletedWith.urn.promise() : undefined;
   833  
   834          return {
   835              resolveURN: resolveURN,
   836              resolveID: resolveID,
   837              resolvers: resolvers,
   838              serializedProps: serializedProps,
   839              parentURN: parentURN,
   840              providerRef: providerRef,
   841              providerRefs: providerRefs,
   842              allDirectDependencyURNs: allDirectDependencyURNs,
   843              propertyToDirectDependencyURNs: propertyToDirectDependencyURNs,
   844              aliases: aliases,
   845              import: importID,
   846              monitorSupportsStructuredAliases,
   847              deletedWithURN,
   848          };
   849      } finally {
   850          // free the RPC queue
   851          done();
   852      }
   853  }
   854  
   855  function addAll<T>(to: Set<T>, from: Set<T>) {
   856      for (const val of from) {
   857          to.add(val);
   858      }
   859  }
   860  
   861  /** @internal */
   862  export async function getAllTransitivelyReferencedResourceURNs(
   863      resources: Set<Resource>,
   864      exclude: Set<Resource>,
   865  ): Promise<Set<string>> {
   866      // Go through 'resources', but transitively walk through **Component** resources, collecting any
   867      // of their child resources.  This way, a Component acts as an aggregation really of all the
   868      // reachable resources it parents.  This walking will stop when it hits custom resources.
   869      //
   870      // This function also terminates at remote components, whose children are not known to the Node SDK directly.
   871      // Remote components will always wait on all of their children, so ensuring we return the remote component
   872      // itself here and waiting on it will accomplish waiting on all of it's children regardless of whether they
   873      // are returned explicitly here.
   874      //
   875      // In other words, if we had:
   876      //
   877      //                  Comp1
   878      //              /     |     \
   879      //          Cust1   Comp2  Remote1
   880      //                  /   \       \
   881      //              Cust2   Cust3  Comp3
   882      //              /                 \
   883      //          Cust4                Cust5
   884      //
   885      // Then the transitively reachable resources of Comp1 will be [Cust1, Cust2, Cust3, Remote1].
   886      // It will *not* include:
   887      // * Cust4 because it is a child of a custom resource
   888      // * Comp2 because it is a non-remote component resource
   889      // * Comp3 and Cust5 because Comp3 is a child of a remote component resource
   890  
   891      // To do this, first we just get the transitively reachable set of resources (not diving
   892      // into custom resources).  In the above picture, if we start with 'Comp1', this will be
   893      // [Comp1, Cust1, Comp2, Cust2, Cust3]
   894      const transitivelyReachableResources = await getTransitivelyReferencedChildResourcesOfComponentResources(
   895          resources,
   896          exclude,
   897      );
   898  
   899      // Then we filter to only include Custom and Remote resources.
   900      const transitivelyReachableCustomResources = [...transitivelyReachableResources].filter(
   901          (r) => (CustomResource.isInstance(r) || (r as ComponentResource).__remote) && !exclude.has(r),
   902      );
   903      const promises = transitivelyReachableCustomResources.map((r) => r.urn.promise());
   904      const urns = await Promise.all(promises);
   905      return new Set<string>(urns);
   906  }
   907  
   908  /**
   909   * Recursively walk the resources passed in, returning them and all resources reachable from
   910   * [Resource.__childResources] through any **Component** resources we encounter.
   911   */
   912  async function getTransitivelyReferencedChildResourcesOfComponentResources(
   913      resources: Set<Resource>,
   914      exclude: Set<Resource>,
   915  ) {
   916      // Recursively walk the dependent resources through their children, adding them to the result set.
   917      const result = new Set<Resource>();
   918      await addTransitivelyReferencedChildResourcesOfComponentResources(resources, exclude, result);
   919      return result;
   920  }
   921  
   922  async function addTransitivelyReferencedChildResourcesOfComponentResources(
   923      resources: Set<Resource> | undefined,
   924      exclude: Set<Resource>,
   925      result: Set<Resource>,
   926  ) {
   927      if (resources) {
   928          for (const resource of resources) {
   929              if (!result.has(resource)) {
   930                  result.add(resource);
   931  
   932                  if (ComponentResource.isInstance(resource)) {
   933                      // Skip including children of a resource in the excluded set to avoid depending on
   934                      // children that haven't been registered yet.
   935                      if (exclude.has(resource)) {
   936                          continue;
   937                      }
   938  
   939                      // This await is safe even if __isConstructed is undefined. Ensure that the
   940                      // resource has completely finished construction.  That way all parent/child
   941                      // relationships will have been setup.
   942                      await resource.__data;
   943                      const children = resource.__childResources;
   944                      addTransitivelyReferencedChildResourcesOfComponentResources(children, exclude, result);
   945                  }
   946              }
   947          }
   948      }
   949  }
   950  
   951  /**
   952   * Gathers explicit dependent Resources from a list of Resources (possibly Promises and/or Outputs).
   953   */
   954  async function gatherExplicitDependencies(
   955      dependsOn: Input<Input<Resource>[]> | Input<Resource> | undefined,
   956  ): Promise<Resource[]> {
   957      if (dependsOn) {
   958          if (Array.isArray(dependsOn)) {
   959              const dos: Resource[] = [];
   960              for (const d of dependsOn) {
   961                  dos.push(...(await gatherExplicitDependencies(d)));
   962              }
   963              return dos;
   964          } else if (dependsOn instanceof Promise) {
   965              return gatherExplicitDependencies(await dependsOn);
   966          } else if (Output.isInstance(dependsOn)) {
   967              // Recursively gather dependencies, await the promise, and append the output's dependencies.
   968              const dos = (dependsOn as Output<Input<Resource>[] | Input<Resource>>).apply((v) =>
   969                  gatherExplicitDependencies(v),
   970              );
   971              const urns = await dos.promise();
   972              const dosResources = await getAllResources(dos);
   973              const implicits = await gatherExplicitDependencies([...dosResources]);
   974              return (urns ?? []).concat(implicits);
   975          } else {
   976              if (!Resource.isInstance(dependsOn)) {
   977                  throw new Error("'dependsOn' was passed a value that was not a Resource.");
   978              }
   979  
   980              return [dependsOn];
   981          }
   982      }
   983  
   984      return [];
   985  }
   986  
   987  /**
   988   * Finishes a resource creation RPC operation by resolving its outputs to the resulting RPC payload.
   989   */
   990  async function resolveOutputs(
   991      res: Resource,
   992      t: string,
   993      name: string,
   994      props: Inputs,
   995      outputs: any,
   996      deps: Record<string, Resource[]>,
   997      resolvers: OutputResolvers,
   998      err?: Error,
   999  ): Promise<void> {
  1000      // Produce a combined set of property states, starting with inputs and then applying
  1001      // outputs.  If the same property exists in the inputs and outputs states, the output wins.
  1002      const allProps: Record<string, any> = {};
  1003      if (outputs) {
  1004          Object.assign(allProps, deserializeProperties(outputs));
  1005      }
  1006  
  1007      const label = `resource:${name}[${t}]#...`;
  1008      if (!isDryRun() || isLegacyApplyEnabled()) {
  1009          for (const key of Object.keys(props)) {
  1010              if (!allProps.hasOwnProperty(key)) {
  1011                  // input prop the engine didn't give us a final value for.  Just use the value passed into the resource
  1012                  // after round-tripping it through serialization. We do the round-tripping primarily s.t. we ensure that
  1013                  // Output values are handled properly w.r.t. unknowns.
  1014                  const inputProp = await serializeProperty(label, props[key], new Set(), { keepOutputValues: false });
  1015                  if (inputProp === undefined) {
  1016                      continue;
  1017                  }
  1018                  allProps[key] = deserializeProperty(inputProp);
  1019              }
  1020          }
  1021      }
  1022  
  1023      resolveProperties(res, resolvers, t, name, allProps, deps, err);
  1024  }
  1025  
  1026  /**
  1027   * registerResourceOutputs completes the resource registration, attaching an optional set of computed outputs.
  1028   */
  1029  export function registerResourceOutputs(res: Resource, outputs: Inputs | Promise<Inputs> | Output<Inputs>) {
  1030      // Now run the operation. Note that we explicitly do not serialize output registration with
  1031      // respect to other resource operations, as outputs may depend on properties of other resources
  1032      // that will not resolve until later turns. This would create a circular promise chain that can
  1033      // never resolve.
  1034      const opLabel = `monitor.registerResourceOutputs(...)`;
  1035      runAsyncResourceOp(
  1036          opLabel,
  1037          async () => {
  1038              // The registration could very well still be taking place, so we will need to wait for its URN.
  1039              // Additionally, the output properties might have come from other resources, so we must await those too.
  1040              const urn = await res.urn.promise();
  1041              const resolved = await serializeProperties(opLabel, { outputs });
  1042              const outputsObj = gstruct.Struct.fromJavaScript(resolved.outputs);
  1043              log.debug(
  1044                  `RegisterResourceOutputs RPC prepared: urn=${urn}` +
  1045                      (excessiveDebugOutput ? `, outputs=${JSON.stringify(outputsObj)}` : ``),
  1046              );
  1047  
  1048              // Fetch the monitor and make an RPC request.
  1049              const monitor = getMonitor();
  1050              if (monitor) {
  1051                  const req = new resproto.RegisterResourceOutputsRequest();
  1052                  req.setUrn(urn);
  1053                  req.setOutputs(outputsObj);
  1054  
  1055                  const label = `monitor.registerResourceOutputs(${urn}, ...)`;
  1056                  await debuggablePromise(
  1057                      new Promise<void>((resolve, reject) =>
  1058                          monitor.registerResourceOutputs(
  1059                              req,
  1060                              (err: grpc.ServiceError | null, innerResponse: gempty.Empty | undefined) => {
  1061                                  log.debug(
  1062                                      `RegisterResourceOutputs RPC finished: urn=${urn}; ` +
  1063                                          `err: ${err}, resp: ${innerResponse}`,
  1064                                  );
  1065                                  if (err) {
  1066                                      // If the monitor is unavailable, it is in the process of shutting down or has already
  1067                                      // shut down. Don't emit an error and don't do any more RPCs, just exit.
  1068                                      if (err.code === grpc.status.UNAVAILABLE || err.code === grpc.status.CANCELLED) {
  1069                                          terminateRpcs();
  1070                                          err.message = "Resource monitor is terminating";
  1071                                      }
  1072  
  1073                                      reject(err);
  1074                                  } else {
  1075                                      log.debug(
  1076                                          `RegisterResourceOutputs RPC finished: urn=${urn}; ` +
  1077                                              `err: ${err}, resp: ${innerResponse}`,
  1078                                      );
  1079                                      resolve();
  1080                                  }
  1081                              },
  1082                          ),
  1083                      ),
  1084                      label,
  1085                  );
  1086              }
  1087          },
  1088          false,
  1089      );
  1090  }
  1091  
  1092  function isAny(o: any): o is any {
  1093      return true;
  1094  }
  1095  
  1096  /**
  1097   * listResourceOutputs returns the resource outputs (if any) for a stack, or an error if the stack
  1098   * cannot be found. Resources are retrieved from the latest stack snapshot, which may include
  1099   * ongoing updates.
  1100   *
  1101   * @param stackName Name of stack to retrieve resource outputs for. Defaults to the current stack.
  1102   * @param typeFilter A [type
  1103   * guard](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards)
  1104   * that specifies which resource types to list outputs of.
  1105   *
  1106   * @example
  1107   * const buckets = pulumi.runtime.listResourceOutput(aws.s3.Bucket.isInstance);
  1108   */
  1109  export function listResourceOutputs<U extends Resource>(
  1110      typeFilter?: (o: any) => o is U,
  1111      stackName?: string,
  1112  ): query.AsyncQueryable<ResolvedResource<U>> {
  1113      if (typeFilter === undefined) {
  1114          typeFilter = isAny;
  1115      }
  1116  
  1117      return query
  1118          .from(
  1119              invoke("pulumi:pulumi:readStackResourceOutputs", {
  1120                  stackName: stackName || getStack(),
  1121              }).then<any[]>(({ outputs }) => utils.values(outputs)),
  1122          )
  1123          .map<ResolvedResource<U>>(({ type: typ, outputs }) => {
  1124              return { ...outputs, __pulumiType: typ };
  1125          })
  1126          .filter(typeFilter);
  1127  }
  1128  
  1129  /**
  1130   * resourceChain is used to serialize all resource requests.  If we don't do this, all resource operations will be
  1131   * entirely asynchronous, meaning the dataflow graph that results will determine ordering of operations.  This
  1132   * causes problems with some resource providers, so for now we will serialize all of them.  The issue
  1133   * pulumi/pulumi#335 tracks coming up with a long-term solution here.
  1134   */
  1135  let resourceChain: Promise<void> = Promise.resolve();
  1136  let resourceChainLabel: string | undefined = undefined;
  1137  
  1138  // runAsyncResourceOp runs an asynchronous resource operation, possibly serializing it as necessary.
  1139  function runAsyncResourceOp(label: string, callback: () => Promise<void>, serial?: boolean): void {
  1140      // Serialize the invocation if necessary.
  1141      if (serial === undefined) {
  1142          serial = serialize();
  1143      }
  1144      const resourceOp: Promise<void> = suppressUnhandledGrpcRejections(
  1145          debuggablePromise(
  1146              resourceChain.then(async () => {
  1147                  if (serial) {
  1148                      resourceChainLabel = label;
  1149                      log.debug(`Resource RPC serialization requested: ${label} is current`);
  1150                  }
  1151                  return callback();
  1152              }),
  1153              label + "-initial",
  1154          ),
  1155      );
  1156  
  1157      // Ensure the process won't exit until this RPC call finishes and resolve it when appropriate.
  1158      const done: () => void = rpcKeepAlive();
  1159      const finalOp: Promise<void> = debuggablePromise(
  1160          resourceOp.then(
  1161              () => {
  1162                  done();
  1163              },
  1164              () => {
  1165                  done();
  1166              },
  1167          ),
  1168          label + "-final",
  1169      );
  1170  
  1171      // Set up another promise that propagates the error, if any, so that it triggers unhandled rejection logic.
  1172      resourceOp.catch((err) => Promise.reject(err));
  1173  
  1174      // If serialization is requested, wait for the prior resource operation to finish before we proceed, serializing
  1175      // them, and make this the current resource operation so that everybody piles up on it.
  1176      if (serial) {
  1177          resourceChain = finalOp;
  1178          if (resourceChainLabel) {
  1179              log.debug(`Resource RPC serialization requested: ${label} is behind ${resourceChainLabel}`);
  1180          }
  1181      }
  1182  }
  1183  
  1184  /**
  1185   * Extract the pkg from the type token of the form "pkg:module:member".
  1186   * @internal
  1187   */
  1188  export function pkgFromType(type: string): string | undefined {
  1189      const parts = type.split(":");
  1190      if (parts.length === 3) {
  1191          return parts[0];
  1192      }
  1193      return undefined;
  1194  }