2018-05-22 19:43:36 +00:00
|
|
|
// Copyright 2016-2018, Pulumi Corporation.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
Implement initial Lumi-as-a-library
This is the initial step towards redefining Lumi as a library that runs
atop vanilla Node.js/V8, rather than as its own runtime.
This change is woefully incomplete but this includes some of the more
stable pieces of my current work-in-progress.
The new structure is that within the sdk/ directory we will have a client
library per language. This client library contains the object model for
Lumi (resources, properties, assets, config, etc), in addition to the
"language runtime host" components required to interoperate with the
Lumi resource monitor. This resource monitor is effectively what we call
"Lumi" today, in that it's the thing orchestrating plans and deployments.
Inside the sdk/ directory, you will find nodejs/, the Node.js client
library, alongside proto/, the definitions for RPC interop between the
different pieces of the system. This includes existing RPC definitions
for resource providers, etc., in addition to the new ones for hosting
different language runtimes from within Lumi.
These new interfaces are surprisingly simple. There is effectively a
bidirectional RPC channel between the Lumi resource monitor, represented
by the lumirpc.ResourceMonitor interface, and each language runtime,
represented by the lumirpc.LanguageRuntime interface.
The overall orchestration goes as follows:
1) Lumi decides it needs to run a program written in language X, so
it dynamically loads the language runtime plugin for language X.
2) Lumi passes that runtime a loopback address to its ResourceMonitor
service, while language X will publish a connection back to its
LanguageRuntime service, which Lumi will talk to.
3) Lumi then invokes LanguageRuntime.Run, passing information like
the desired working directory, program name, arguments, and optional
configuration variables to make available to the program.
4) The language X runtime receives this, unpacks it and sets up the
necessary context, and then invokes the program. The program then
calls into Lumi object model abstractions that internally communicate
back to Lumi using the ResourceMonitor interface.
5) The key here is ResourceMonitor.NewResource, which Lumi uses to
serialize state about newly allocated resources. Lumi receives these
and registers them as part of the plan, doing the usual diffing, etc.,
to decide how to proceed. This interface is perhaps one of the
most subtle parts of the new design, as it necessitates the use of
promises internally to allow parallel evaluation of the resource plan,
letting dataflow determine the available concurrency.
6) The program exits, and Lumi continues on its merry way. If the program
fails, the RunResponse will include information about the failure.
Due to (5), all properties on resources are now instances of a new
Property<T> type. A Property<T> is just a thin wrapper over a T, but it
encodes the special properties of Lumi resource properties. Namely, it
is possible to create one out of a T, other Property<T>, Promise<T>, or
to freshly allocate one. In all cases, the Property<T> does not "settle"
until its final state is known. This cannot occur before the deployment
actually completes, and so in general it's not safe to depend on concrete
resolutions of values (unlike ordinary Promise<T>s which are usually
expected to resolve). As a result, all derived computations are meant to
use the `then` function (as in `someValue.then(v => v+x)`).
Although this change includes tests that may be run in isolation to test
the various RPC interactions, we are nowhere near finished. The remaining
work primarily boils down to three things:
1) Wiring all of this up to the Lumi code.
2) Fixing the handful of known loose ends required to make this work,
primarily around the serialization of properties (waiting on
unresolved ones, serializing assets properly, etc).
3) Implementing lambda closure serialization as a native extension.
This ongoing work is part of pulumi/pulumi-fabric#311.
2017-08-26 19:07:54 +00:00
|
|
|
|
2024-03-07 08:52:34 +00:00
|
|
|
import * as url from "url";
|
2019-10-15 05:08:06 +00:00
|
|
|
import { ResourceError } from "./errors";
|
2023-06-23 21:32:03 +00:00
|
|
|
import * as log from "./log";
|
2019-10-15 05:08:06 +00:00
|
|
|
import { Input, Inputs, interpolate, Output, output } from "./output";
|
2023-07-10 17:59:15 +00:00
|
|
|
import {
|
|
|
|
getResource,
|
|
|
|
pkgFromType,
|
|
|
|
readResource,
|
|
|
|
registerResource,
|
|
|
|
registerResourceOutputs,
|
|
|
|
SourcePosition,
|
|
|
|
} from "./runtime/resource";
|
2023-06-23 21:32:03 +00:00
|
|
|
import { unknownValue } from "./runtime/rpc";
|
2019-06-01 06:01:01 +00:00
|
|
|
import { getProject, getStack } from "./runtime/settings";
|
2022-09-01 18:39:09 +00:00
|
|
|
import { getStackResource } from "./runtime/state";
|
2019-02-28 22:56:35 +00:00
|
|
|
import * as utils from "./utils";
|
Implement initial Lumi-as-a-library
This is the initial step towards redefining Lumi as a library that runs
atop vanilla Node.js/V8, rather than as its own runtime.
This change is woefully incomplete but this includes some of the more
stable pieces of my current work-in-progress.
The new structure is that within the sdk/ directory we will have a client
library per language. This client library contains the object model for
Lumi (resources, properties, assets, config, etc), in addition to the
"language runtime host" components required to interoperate with the
Lumi resource monitor. This resource monitor is effectively what we call
"Lumi" today, in that it's the thing orchestrating plans and deployments.
Inside the sdk/ directory, you will find nodejs/, the Node.js client
library, alongside proto/, the definitions for RPC interop between the
different pieces of the system. This includes existing RPC definitions
for resource providers, etc., in addition to the new ones for hosting
different language runtimes from within Lumi.
These new interfaces are surprisingly simple. There is effectively a
bidirectional RPC channel between the Lumi resource monitor, represented
by the lumirpc.ResourceMonitor interface, and each language runtime,
represented by the lumirpc.LanguageRuntime interface.
The overall orchestration goes as follows:
1) Lumi decides it needs to run a program written in language X, so
it dynamically loads the language runtime plugin for language X.
2) Lumi passes that runtime a loopback address to its ResourceMonitor
service, while language X will publish a connection back to its
LanguageRuntime service, which Lumi will talk to.
3) Lumi then invokes LanguageRuntime.Run, passing information like
the desired working directory, program name, arguments, and optional
configuration variables to make available to the program.
4) The language X runtime receives this, unpacks it and sets up the
necessary context, and then invokes the program. The program then
calls into Lumi object model abstractions that internally communicate
back to Lumi using the ResourceMonitor interface.
5) The key here is ResourceMonitor.NewResource, which Lumi uses to
serialize state about newly allocated resources. Lumi receives these
and registers them as part of the plan, doing the usual diffing, etc.,
to decide how to proceed. This interface is perhaps one of the
most subtle parts of the new design, as it necessitates the use of
promises internally to allow parallel evaluation of the resource plan,
letting dataflow determine the available concurrency.
6) The program exits, and Lumi continues on its merry way. If the program
fails, the RunResponse will include information about the failure.
Due to (5), all properties on resources are now instances of a new
Property<T> type. A Property<T> is just a thin wrapper over a T, but it
encodes the special properties of Lumi resource properties. Namely, it
is possible to create one out of a T, other Property<T>, Promise<T>, or
to freshly allocate one. In all cases, the Property<T> does not "settle"
until its final state is known. This cannot occur before the deployment
actually completes, and so in general it's not safe to depend on concrete
resolutions of values (unlike ordinary Promise<T>s which are usually
expected to resolve). As a result, all derived computations are meant to
use the `then` function (as in `someValue.then(v => v+x)`).
Although this change includes tests that may be run in isolation to test
the various RPC interactions, we are nowhere near finished. The remaining
work primarily boils down to three things:
1) Wiring all of this up to the Lumi code.
2) Fixing the handful of known loose ends required to make this work,
primarily around the serialization of properties (waiting on
unresolved ones, serializing assets properly, etc).
3) Implementing lambda closure serialization as a native extension.
This ongoing work is part of pulumi/pulumi-fabric#311.
2017-08-26 19:07:54 +00:00
|
|
|
|
2023-04-28 22:27:10 +00:00
|
|
|
export type ID = string; // a provider-assigned ID.
|
Implement initial Lumi-as-a-library
This is the initial step towards redefining Lumi as a library that runs
atop vanilla Node.js/V8, rather than as its own runtime.
This change is woefully incomplete but this includes some of the more
stable pieces of my current work-in-progress.
The new structure is that within the sdk/ directory we will have a client
library per language. This client library contains the object model for
Lumi (resources, properties, assets, config, etc), in addition to the
"language runtime host" components required to interoperate with the
Lumi resource monitor. This resource monitor is effectively what we call
"Lumi" today, in that it's the thing orchestrating plans and deployments.
Inside the sdk/ directory, you will find nodejs/, the Node.js client
library, alongside proto/, the definitions for RPC interop between the
different pieces of the system. This includes existing RPC definitions
for resource providers, etc., in addition to the new ones for hosting
different language runtimes from within Lumi.
These new interfaces are surprisingly simple. There is effectively a
bidirectional RPC channel between the Lumi resource monitor, represented
by the lumirpc.ResourceMonitor interface, and each language runtime,
represented by the lumirpc.LanguageRuntime interface.
The overall orchestration goes as follows:
1) Lumi decides it needs to run a program written in language X, so
it dynamically loads the language runtime plugin for language X.
2) Lumi passes that runtime a loopback address to its ResourceMonitor
service, while language X will publish a connection back to its
LanguageRuntime service, which Lumi will talk to.
3) Lumi then invokes LanguageRuntime.Run, passing information like
the desired working directory, program name, arguments, and optional
configuration variables to make available to the program.
4) The language X runtime receives this, unpacks it and sets up the
necessary context, and then invokes the program. The program then
calls into Lumi object model abstractions that internally communicate
back to Lumi using the ResourceMonitor interface.
5) The key here is ResourceMonitor.NewResource, which Lumi uses to
serialize state about newly allocated resources. Lumi receives these
and registers them as part of the plan, doing the usual diffing, etc.,
to decide how to proceed. This interface is perhaps one of the
most subtle parts of the new design, as it necessitates the use of
promises internally to allow parallel evaluation of the resource plan,
letting dataflow determine the available concurrency.
6) The program exits, and Lumi continues on its merry way. If the program
fails, the RunResponse will include information about the failure.
Due to (5), all properties on resources are now instances of a new
Property<T> type. A Property<T> is just a thin wrapper over a T, but it
encodes the special properties of Lumi resource properties. Namely, it
is possible to create one out of a T, other Property<T>, Promise<T>, or
to freshly allocate one. In all cases, the Property<T> does not "settle"
until its final state is known. This cannot occur before the deployment
actually completes, and so in general it's not safe to depend on concrete
resolutions of values (unlike ordinary Promise<T>s which are usually
expected to resolve). As a result, all derived computations are meant to
use the `then` function (as in `someValue.then(v => v+x)`).
Although this change includes tests that may be run in isolation to test
the various RPC interactions, we are nowhere near finished. The remaining
work primarily boils down to three things:
1) Wiring all of this up to the Lumi code.
2) Fixing the handful of known loose ends required to make this work,
primarily around the serialization of properties (waiting on
unresolved ones, serializing assets properly, etc).
3) Implementing lambda closure serialization as a native extension.
This ongoing work is part of pulumi/pulumi-fabric#311.
2017-08-26 19:07:54 +00:00
|
|
|
export type URN = string; // an automatically generated logical URN, used to stably identify resources.
|
|
|
|
|
2019-06-01 06:01:01 +00:00
|
|
|
/**
|
|
|
|
* createUrn computes a URN from the combination of a resource name, resource type, optional parent,
|
|
|
|
* optional project and optional stack.
|
|
|
|
*/
|
2023-04-28 22:27:10 +00:00
|
|
|
export function createUrn(
|
|
|
|
name: Input<string>,
|
|
|
|
type: Input<string>,
|
|
|
|
parent?: Resource | Input<URN>,
|
|
|
|
project?: string,
|
|
|
|
stack?: string,
|
|
|
|
): Output<string> {
|
2019-06-01 06:01:01 +00:00
|
|
|
let parentPrefix: Output<string>;
|
|
|
|
if (parent) {
|
|
|
|
let parentUrn: Output<string>;
|
|
|
|
if (Resource.isInstance(parent)) {
|
|
|
|
parentUrn = parent.urn;
|
|
|
|
} else {
|
|
|
|
parentUrn = output(parent);
|
|
|
|
}
|
2023-04-28 22:27:10 +00:00
|
|
|
parentPrefix = parentUrn.apply((parentUrnString) => {
|
2022-06-30 15:13:04 +00:00
|
|
|
const prefix = parentUrnString.substring(0, parentUrnString.lastIndexOf("::")) + "$";
|
|
|
|
if (prefix.endsWith("::pulumi:pulumi:Stack$")) {
|
|
|
|
// Don't prefix the stack type as a parent type
|
|
|
|
return `urn:pulumi:${stack || getStack()}::${project || getProject()}::`;
|
|
|
|
}
|
|
|
|
return prefix;
|
|
|
|
});
|
2019-06-01 06:01:01 +00:00
|
|
|
} else {
|
|
|
|
parentPrefix = output(`urn:pulumi:${stack || getStack()}::${project || getProject()}::`);
|
|
|
|
}
|
|
|
|
return interpolate`${parentPrefix}${type}::${name}`;
|
|
|
|
}
|
|
|
|
|
2024-06-03 12:50:03 +00:00
|
|
|
/**
|
|
|
|
* inheritedChildAlias computes the alias that should be applied to a child based on an alias applied to it's parent.
|
|
|
|
* This may involve changing the name of the resource in cases where the resource has a named derived from the name of
|
|
|
|
* the parent, and the parent name changed.
|
|
|
|
*/
|
2023-04-28 22:27:10 +00:00
|
|
|
function inheritedChildAlias(
|
|
|
|
childName: string,
|
|
|
|
parentName: string,
|
|
|
|
parentAlias: Input<string>,
|
|
|
|
childType: string,
|
|
|
|
): Output<string> {
|
2019-06-01 06:01:01 +00:00
|
|
|
// If the child name has the parent name as a prefix, then we make the assumption that it was
|
|
|
|
// constructed from the convention of using `{name}-details` as the name of the child resource. To
|
|
|
|
// ensure this is aliased correctly, we must then also replace the parent aliases name in the prefix of
|
|
|
|
// the child resource name.
|
|
|
|
//
|
|
|
|
// For example:
|
|
|
|
// * name: "newapp-function"
|
|
|
|
// * opts.parent.__name: "newapp"
|
|
|
|
// * parentAlias: "urn:pulumi:stackname::projectname::awsx:ec2:Vpc::app"
|
|
|
|
// * parentAliasName: "app"
|
|
|
|
// * aliasName: "app-function"
|
|
|
|
// * childAlias: "urn:pulumi:stackname::projectname::aws:s3/bucket:Bucket::app-function"
|
|
|
|
let aliasName = output(childName);
|
|
|
|
if (childName.startsWith(parentName)) {
|
2023-04-28 22:27:10 +00:00
|
|
|
aliasName = output(parentAlias).apply((parentAliasUrn) => {
|
2019-06-01 06:01:01 +00:00
|
|
|
const parentAliasName = parentAliasUrn.substring(parentAliasUrn.lastIndexOf("::") + 2);
|
|
|
|
return parentAliasName + childName.substring(parentName.length);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return createUrn(aliasName, childType, parentAlias);
|
|
|
|
}
|
|
|
|
|
2024-06-03 12:50:03 +00:00
|
|
|
/**
|
|
|
|
* Extracts the type and name from a URN.
|
|
|
|
*/
|
2021-12-29 17:07:55 +00:00
|
|
|
function urnTypeAndName(urn: URN) {
|
|
|
|
const parts = urn.split("::");
|
|
|
|
const typeParts = parts[2].split("$");
|
|
|
|
return {
|
|
|
|
name: parts[3],
|
2023-04-28 22:27:10 +00:00
|
|
|
type: typeParts[typeParts.length - 1],
|
2021-12-29 17:07:55 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-06-03 12:50:03 +00:00
|
|
|
/**
|
|
|
|
* allAliases computes the full set of aliases for a child resource given a set of aliases applied to the child and
|
|
|
|
* parent resources. This includes the child resource's own aliases, as well as aliases inherited from the parent.
|
|
|
|
* If there are N child aliases, and M parent aliases, there will be (M+1)*(N+1)-1 total aliases,
|
|
|
|
* or, as calculated in the logic below, N+(M*(1+N)).
|
|
|
|
*/
|
2023-04-28 22:27:10 +00:00
|
|
|
export function allAliases(
|
|
|
|
childAliases: Input<URN | Alias>[],
|
|
|
|
childName: string,
|
|
|
|
childType: string,
|
|
|
|
parent: Resource,
|
|
|
|
parentName: string,
|
|
|
|
): Output<URN>[] {
|
2021-12-29 17:07:55 +00:00
|
|
|
const aliases: Output<URN>[] = [];
|
|
|
|
for (const childAlias of childAliases) {
|
|
|
|
aliases.push(collapseAliasToUrn(childAlias, childName, childType, parent));
|
|
|
|
}
|
2023-04-28 22:27:10 +00:00
|
|
|
for (const parentAlias of parent.__aliases || []) {
|
2021-12-29 17:07:55 +00:00
|
|
|
// For each parent alias, add an alias that uses that base child name and the parent alias
|
|
|
|
aliases.push(inheritedChildAlias(childName, parentName, parentAlias, childType));
|
|
|
|
// Also add an alias for each child alias and the parent alias
|
|
|
|
for (const childAlias of childAliases) {
|
2023-04-28 22:27:10 +00:00
|
|
|
const inheritedAlias = collapseAliasToUrn(childAlias, childName, childType, parent).apply(
|
|
|
|
(childAliasURN) => {
|
|
|
|
const { name: aliasedChildName, type: aliasedChildType } = urnTypeAndName(childAliasURN);
|
|
|
|
return inheritedChildAlias(aliasedChildName, parentName, parentAlias, aliasedChildType);
|
|
|
|
},
|
|
|
|
);
|
2021-12-29 17:07:55 +00:00
|
|
|
aliases.push(inheritedAlias);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return aliases;
|
|
|
|
}
|
|
|
|
|
2017-09-22 01:15:29 +00:00
|
|
|
/**
|
|
|
|
* Resource represents a class whose CRUD operations are implemented by a provider plugin.
|
|
|
|
*/
|
Implement initial Lumi-as-a-library
This is the initial step towards redefining Lumi as a library that runs
atop vanilla Node.js/V8, rather than as its own runtime.
This change is woefully incomplete but this includes some of the more
stable pieces of my current work-in-progress.
The new structure is that within the sdk/ directory we will have a client
library per language. This client library contains the object model for
Lumi (resources, properties, assets, config, etc), in addition to the
"language runtime host" components required to interoperate with the
Lumi resource monitor. This resource monitor is effectively what we call
"Lumi" today, in that it's the thing orchestrating plans and deployments.
Inside the sdk/ directory, you will find nodejs/, the Node.js client
library, alongside proto/, the definitions for RPC interop between the
different pieces of the system. This includes existing RPC definitions
for resource providers, etc., in addition to the new ones for hosting
different language runtimes from within Lumi.
These new interfaces are surprisingly simple. There is effectively a
bidirectional RPC channel between the Lumi resource monitor, represented
by the lumirpc.ResourceMonitor interface, and each language runtime,
represented by the lumirpc.LanguageRuntime interface.
The overall orchestration goes as follows:
1) Lumi decides it needs to run a program written in language X, so
it dynamically loads the language runtime plugin for language X.
2) Lumi passes that runtime a loopback address to its ResourceMonitor
service, while language X will publish a connection back to its
LanguageRuntime service, which Lumi will talk to.
3) Lumi then invokes LanguageRuntime.Run, passing information like
the desired working directory, program name, arguments, and optional
configuration variables to make available to the program.
4) The language X runtime receives this, unpacks it and sets up the
necessary context, and then invokes the program. The program then
calls into Lumi object model abstractions that internally communicate
back to Lumi using the ResourceMonitor interface.
5) The key here is ResourceMonitor.NewResource, which Lumi uses to
serialize state about newly allocated resources. Lumi receives these
and registers them as part of the plan, doing the usual diffing, etc.,
to decide how to proceed. This interface is perhaps one of the
most subtle parts of the new design, as it necessitates the use of
promises internally to allow parallel evaluation of the resource plan,
letting dataflow determine the available concurrency.
6) The program exits, and Lumi continues on its merry way. If the program
fails, the RunResponse will include information about the failure.
Due to (5), all properties on resources are now instances of a new
Property<T> type. A Property<T> is just a thin wrapper over a T, but it
encodes the special properties of Lumi resource properties. Namely, it
is possible to create one out of a T, other Property<T>, Promise<T>, or
to freshly allocate one. In all cases, the Property<T> does not "settle"
until its final state is known. This cannot occur before the deployment
actually completes, and so in general it's not safe to depend on concrete
resolutions of values (unlike ordinary Promise<T>s which are usually
expected to resolve). As a result, all derived computations are meant to
use the `then` function (as in `someValue.then(v => v+x)`).
Although this change includes tests that may be run in isolation to test
the various RPC interactions, we are nowhere near finished. The remaining
work primarily boils down to three things:
1) Wiring all of this up to the Lumi code.
2) Fixing the handful of known loose ends required to make this work,
primarily around the serialization of properties (waiting on
unresolved ones, serializing assets properly, etc).
3) Implementing lambda closure serialization as a native extension.
This ongoing work is part of pulumi/pulumi-fabric#311.
2017-08-26 19:07:54 +00:00
|
|
|
export abstract class Resource {
|
2023-07-10 17:59:15 +00:00
|
|
|
/**
|
|
|
|
* A regexp for use with sourcePosition.
|
|
|
|
*/
|
|
|
|
private static sourcePositionRegExp =
|
|
|
|
/Error:\s*\n\s*at new Resource \(.*\)\n\s*at new \S*Resource \(.*\)\n(\s*at new \S* \(.*\)\n)?[^(]*\((?<file>.*):(?<line>[0-9]+):(?<col>[0-9]+)\)\n/;
|
|
|
|
|
2018-04-25 00:23:18 +00:00
|
|
|
/**
|
|
|
|
* A private field to help with RTTI that works in SxS scenarios.
|
2020-01-26 17:06:35 +00:00
|
|
|
* @internal
|
2018-04-25 00:23:18 +00:00
|
|
|
*/
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2019-06-01 06:01:01 +00:00
|
|
|
public readonly __pulumiResource: boolean = true;
|
2018-04-25 00:23:18 +00:00
|
|
|
|
2019-03-06 01:06:57 +00:00
|
|
|
/**
|
|
|
|
* The optional parent of this resource.
|
2020-01-26 17:06:35 +00:00
|
|
|
* @internal
|
2019-03-06 01:06:57 +00:00
|
|
|
*/
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2019-04-24 02:24:06 +00:00
|
|
|
public readonly __parentResource: Resource | undefined;
|
2019-03-06 01:06:57 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
2020-01-26 17:06:35 +00:00
|
|
|
*
|
|
|
|
* @internal
|
2019-03-06 01:06:57 +00:00
|
|
|
*/
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2019-04-24 02:24:06 +00:00
|
|
|
public __childResources: Set<Resource> | undefined;
|
2019-03-06 01:06:57 +00:00
|
|
|
|
2017-09-22 01:15:29 +00:00
|
|
|
/**
|
2018-01-26 02:50:58 +00:00
|
|
|
* urn is the stable logical URN used to distinctly address a resource, both before and after
|
|
|
|
* deployments.
|
2017-09-22 01:15:29 +00:00
|
|
|
*/
|
2019-09-11 23:21:35 +00:00
|
|
|
public readonly urn!: Output<URN>;
|
Implement components
This change implements core support for "components" in the Pulumi
Fabric. This work is described further in pulumi/pulumi#340, where
we are still discussing some of the finer points.
In a nutshell, resources no longer imply external providers. It's
entirely possible to have a resource that logically represents
something but without having a physical manifestation that needs to
be tracked and managed by our typical CRUD operations.
For example, the aws/serverless/Function helper is one such type.
It aggregates Lambda-related resources and exposes a nice interface.
All of the Pulumi Cloud Framework resources are also examples.
To indicate that a resource does participate in the usual CRUD resource
provider, it simply derives from ExternalResource instead of Resource.
All resources now have the ability to adopt children. This is purely
a metadata/tagging thing, and will help us roll up displays, provide
attribution to the developer, and even hide aspects of the resource
graph as appropriate (e.g., when they are implementation details).
Our use of this capability is ultra limited right now; in fact, the
only place we display children is in the CLI output. For instance:
+ aws:serverless:Function: (create)
[urn=urn:pulumi:demo::serverless::aws:serverless:Function::mylambda]
=> urn:pulumi:demo::serverless::aws:iam/role:Role::mylambda-iamrole
=> urn:pulumi:demo::serverless::aws:iam/rolePolicyAttachment:RolePolicyAttachment::mylambda-iampolicy-0
=> urn:pulumi:demo::serverless::aws:lambda/function:Function::mylambda
The bit indicating whether a resource is external or not is tracked
in the resulting checkpoint file, along with any of its children.
2017-10-14 21:18:43 +00:00
|
|
|
|
2018-08-10 23:18:21 +00:00
|
|
|
/**
|
|
|
|
* When set to true, protect ensures this resource cannot be deleted.
|
2020-01-26 17:06:35 +00:00
|
|
|
* @internal
|
2018-08-10 23:18:21 +00:00
|
|
|
*/
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2019-04-24 02:24:06 +00:00
|
|
|
private readonly __protect: boolean;
|
2018-08-10 23:18:21 +00:00
|
|
|
|
Transformations (#3174)
Adds the ability to provide `transformations` to modify the properties and resource options that will be used for any child resource of a component or stack.
This offers an "escape hatch" to modify the behaviour of a component by peeking behind it's abstraction. For example, it can be used to add a resource option (`additionalSecretOutputs`, `aliases`, `protect`, etc.) to a specific known child of a component, or to modify some input property to a child resource if the component does not (yet) expose the ability to control that input directly. It could also be used for more interesting scenarios - such as:
1. Automatically applying tags to all resources that support them in a stack (or component)
2. Injecting real dependencies between stringly-referenced resources in a Helm Chart
3. Injecting explicit names using a preferred naming convention across all resources in a stack
4. Injecting `import` onto all resources by doing a lookup into a name=>id mapping
Because this feature makes it possible to peek behind a component abstraction, it must be used with care in cases where the component is versioned independently of the use of transformations. Also, this can result in "spooky action at a distance", so should be used judiciously. That said - this can be used as an escape hatch to unblock a wide variety of common use cases without waiting on changes to be made in a component implementation.
Each transformation is passed the `resource`, `name`, `type`, `props` and `opts` that are passed into the `Resource` constructor for any resource descended from the resource that has the transformation applied. The transformation callback can optionally return alternate versions of the `props` and `opts` to be used in place of the original values provided to the resource constructor.
Fixes #2068.
2019-09-29 18:27:37 +00:00
|
|
|
/**
|
|
|
|
* A collection of transformations to apply as part of resource registration.
|
|
|
|
*
|
|
|
|
* Note: This is marked optional only because older versions of this library may not have had
|
|
|
|
* this property, and marking optional forces consumers of the property to defensively handle
|
|
|
|
* cases where they are passed "old" resources.
|
2020-01-26 17:06:35 +00:00
|
|
|
* @internal
|
Transformations (#3174)
Adds the ability to provide `transformations` to modify the properties and resource options that will be used for any child resource of a component or stack.
This offers an "escape hatch" to modify the behaviour of a component by peeking behind it's abstraction. For example, it can be used to add a resource option (`additionalSecretOutputs`, `aliases`, `protect`, etc.) to a specific known child of a component, or to modify some input property to a child resource if the component does not (yet) expose the ability to control that input directly. It could also be used for more interesting scenarios - such as:
1. Automatically applying tags to all resources that support them in a stack (or component)
2. Injecting real dependencies between stringly-referenced resources in a Helm Chart
3. Injecting explicit names using a preferred naming convention across all resources in a stack
4. Injecting `import` onto all resources by doing a lookup into a name=>id mapping
Because this feature makes it possible to peek behind a component abstraction, it must be used with care in cases where the component is versioned independently of the use of transformations. Also, this can result in "spooky action at a distance", so should be used judiciously. That said - this can be used as an escape hatch to unblock a wide variety of common use cases without waiting on changes to be made in a component implementation.
Each transformation is passed the `resource`, `name`, `type`, `props` and `opts` that are passed into the `Resource` constructor for any resource descended from the resource that has the transformation applied. The transformation callback can optionally return alternate versions of the `props` and `opts` to be used in place of the original values provided to the resource constructor.
Fixes #2068.
2019-09-29 18:27:37 +00:00
|
|
|
*/
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
Transformations (#3174)
Adds the ability to provide `transformations` to modify the properties and resource options that will be used for any child resource of a component or stack.
This offers an "escape hatch" to modify the behaviour of a component by peeking behind it's abstraction. For example, it can be used to add a resource option (`additionalSecretOutputs`, `aliases`, `protect`, etc.) to a specific known child of a component, or to modify some input property to a child resource if the component does not (yet) expose the ability to control that input directly. It could also be used for more interesting scenarios - such as:
1. Automatically applying tags to all resources that support them in a stack (or component)
2. Injecting real dependencies between stringly-referenced resources in a Helm Chart
3. Injecting explicit names using a preferred naming convention across all resources in a stack
4. Injecting `import` onto all resources by doing a lookup into a name=>id mapping
Because this feature makes it possible to peek behind a component abstraction, it must be used with care in cases where the component is versioned independently of the use of transformations. Also, this can result in "spooky action at a distance", so should be used judiciously. That said - this can be used as an escape hatch to unblock a wide variety of common use cases without waiting on changes to be made in a component implementation.
Each transformation is passed the `resource`, `name`, `type`, `props` and `opts` that are passed into the `Resource` constructor for any resource descended from the resource that has the transformation applied. The transformation callback can optionally return alternate versions of the `props` and `opts` to be used in place of the original values provided to the resource constructor.
Fixes #2068.
2019-09-29 18:27:37 +00:00
|
|
|
__transformations?: ResourceTransformation[];
|
|
|
|
|
2019-06-01 06:01:01 +00:00
|
|
|
/**
|
|
|
|
* A list of aliases applied to this resource.
|
2019-07-16 18:15:26 +00:00
|
|
|
*
|
|
|
|
* Note: This is marked optional only because older versions of this library may not have had
|
Transformations (#3174)
Adds the ability to provide `transformations` to modify the properties and resource options that will be used for any child resource of a component or stack.
This offers an "escape hatch" to modify the behaviour of a component by peeking behind it's abstraction. For example, it can be used to add a resource option (`additionalSecretOutputs`, `aliases`, `protect`, etc.) to a specific known child of a component, or to modify some input property to a child resource if the component does not (yet) expose the ability to control that input directly. It could also be used for more interesting scenarios - such as:
1. Automatically applying tags to all resources that support them in a stack (or component)
2. Injecting real dependencies between stringly-referenced resources in a Helm Chart
3. Injecting explicit names using a preferred naming convention across all resources in a stack
4. Injecting `import` onto all resources by doing a lookup into a name=>id mapping
Because this feature makes it possible to peek behind a component abstraction, it must be used with care in cases where the component is versioned independently of the use of transformations. Also, this can result in "spooky action at a distance", so should be used judiciously. That said - this can be used as an escape hatch to unblock a wide variety of common use cases without waiting on changes to be made in a component implementation.
Each transformation is passed the `resource`, `name`, `type`, `props` and `opts` that are passed into the `Resource` constructor for any resource descended from the resource that has the transformation applied. The transformation callback can optionally return alternate versions of the `props` and `opts` to be used in place of the original values provided to the resource constructor.
Fixes #2068.
2019-09-29 18:27:37 +00:00
|
|
|
* this property, and marking optional forces consumers of the property to defensively handle
|
2019-07-16 18:15:26 +00:00
|
|
|
* cases where they are passed "old" resources.
|
2020-01-26 17:06:35 +00:00
|
|
|
* @internal
|
2019-06-01 06:01:01 +00:00
|
|
|
*/
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2019-07-16 18:15:26 +00:00
|
|
|
readonly __aliases?: Input<URN>[];
|
2019-06-01 06:01:01 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The name assigned to the resource at construction.
|
2019-07-16 18:15:26 +00:00
|
|
|
*
|
|
|
|
* Note: This is marked optional only because older versions of this library may not have had
|
Transformations (#3174)
Adds the ability to provide `transformations` to modify the properties and resource options that will be used for any child resource of a component or stack.
This offers an "escape hatch" to modify the behaviour of a component by peeking behind it's abstraction. For example, it can be used to add a resource option (`additionalSecretOutputs`, `aliases`, `protect`, etc.) to a specific known child of a component, or to modify some input property to a child resource if the component does not (yet) expose the ability to control that input directly. It could also be used for more interesting scenarios - such as:
1. Automatically applying tags to all resources that support them in a stack (or component)
2. Injecting real dependencies between stringly-referenced resources in a Helm Chart
3. Injecting explicit names using a preferred naming convention across all resources in a stack
4. Injecting `import` onto all resources by doing a lookup into a name=>id mapping
Because this feature makes it possible to peek behind a component abstraction, it must be used with care in cases where the component is versioned independently of the use of transformations. Also, this can result in "spooky action at a distance", so should be used judiciously. That said - this can be used as an escape hatch to unblock a wide variety of common use cases without waiting on changes to be made in a component implementation.
Each transformation is passed the `resource`, `name`, `type`, `props` and `opts` that are passed into the `Resource` constructor for any resource descended from the resource that has the transformation applied. The transformation callback can optionally return alternate versions of the `props` and `opts` to be used in place of the original values provided to the resource constructor.
Fixes #2068.
2019-09-29 18:27:37 +00:00
|
|
|
* this property, and marking optional forces consumers of the property to defensively handle
|
2019-07-16 18:15:26 +00:00
|
|
|
* cases where they are passed "old" resources.
|
2020-01-26 17:06:35 +00:00
|
|
|
* @internal
|
2019-06-01 06:01:01 +00:00
|
|
|
*/
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2022-10-31 18:25:24 +00:00
|
|
|
readonly __name?: string;
|
2019-06-01 06:01:01 +00:00
|
|
|
|
2018-08-10 23:18:21 +00:00
|
|
|
/**
|
|
|
|
* The set of providers to use for child resources. Keyed by package name (e.g. "aws").
|
2020-01-26 17:06:35 +00:00
|
|
|
* @internal
|
2018-08-10 23:18:21 +00:00
|
|
|
*/
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2019-04-24 02:24:06 +00:00
|
|
|
private readonly __providers: Record<string, ProviderResource>;
|
2018-08-10 23:18:21 +00:00
|
|
|
|
2021-07-07 23:03:56 +00:00
|
|
|
/**
|
[sdk/nodejs] Fix provider for resource methods (#13796)
The `Resource` class in the Node.js SDK has the following internal
property:
```typescript
/** @internal */
readonly __prov?: ProviderResource;
```
When a resource is created, the provider specified for the resource is
stored in this property. If it is set, it is passed along in the `Call`
request when a method is called on the resource.
Prior to #13282, the property was only set for custom resources in
`Resource`'s constructor:
```typescript
this.__prov = custom ? opts.provider : undefined;
```
With #13282, it was changed to also store the value for remote
components:
```diff
- this.__prov = custom ? opts.provider : undefined;
+ this.__prov = custom || remote ? opts.provider : undefined;
```
This regressed the behavior when calling a method on a remote component
that had an explicit provider that wasn't the component provider, but
some other provider (e.g. AWS provider) specified as:
```typescript
const component = new MyRemoteComponent("comp", {
}, { provider: awsProvider });
```
The `awsProvider` was being stored in `Resource.__prov`, and when making
the method call on the resource, it would try to invoke `Call` on the
AWS provider, rather than calling the remote component provider's
`Call`, which resulted in an error.
Note that specifying the AWS provider using the more verbose `providers:
[awsProvider]` works around the issue.
The fix is to only set `__prov` if the provider's package is the same as
the resource's package. Otherwise, don't set it, because the user is
specifying a provider with the `provider: awsProvider` syntax as
shorthand for `providers: [awsProvider]`.
Fixes #13777
2023-08-30 14:49:53 +00:00
|
|
|
* The specified provider or provider determined from the parent for custom or remote resources.
|
|
|
|
* It is passed along in the `Call` gRPC request for resource method calls (when set) so that the
|
|
|
|
* call goes to the same provider as the resource.
|
2021-07-07 23:03:56 +00:00
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
// Note: This is deliberately not named `__provider` as that conflicts with the property
|
|
|
|
// used by the `dynamic.Resource` class.
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2021-07-07 23:03:56 +00:00
|
|
|
readonly __prov?: ProviderResource;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The specified provider version.
|
|
|
|
* @internal
|
|
|
|
*/
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2021-07-07 23:03:56 +00:00
|
|
|
readonly __version?: string;
|
|
|
|
|
2022-01-10 23:54:41 +00:00
|
|
|
/**
|
|
|
|
* The specified provider download URL.
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
|
|
|
readonly __pluginDownloadURL?: string;
|
|
|
|
|
2023-06-23 21:32:03 +00:00
|
|
|
/**
|
|
|
|
* Private field containing the type ID for this object. Useful for implementing `isInstance` on
|
|
|
|
* classes that inherit from `Resource`.
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
|
|
|
public readonly __pulumiType: string;
|
|
|
|
|
2018-04-25 00:23:18 +00:00
|
|
|
public static isInstance(obj: any): obj is Resource {
|
2019-02-28 22:56:35 +00:00
|
|
|
return utils.isInstance<Resource>(obj, "__pulumiResource");
|
2018-04-25 00:23:18 +00:00
|
|
|
}
|
|
|
|
|
2024-06-03 12:50:03 +00:00
|
|
|
/**
|
|
|
|
* sourcePosition returns the source position of the user code that instantiated this resource.
|
|
|
|
*
|
|
|
|
* This is somewhat brittle in that it expects a call stack of the form:
|
|
|
|
* - Resource class constructor
|
|
|
|
* - abstract Resource subclass constructor
|
|
|
|
* - concrete Resource subclass constructor
|
|
|
|
* - user code
|
|
|
|
*
|
|
|
|
* This stack reflects the expected class hierarchy of "cloud resource / component resource < customresource/componentresource < resource".
|
|
|
|
*
|
|
|
|
* For example, consider the AWS S3 Bucket resource. When user code instantiates a Bucket, the stack will look like
|
|
|
|
* this:
|
|
|
|
*
|
|
|
|
* new Resource (/path/to/resource.ts:123:45)
|
|
|
|
* new CustomResource (/path/to/resource.ts:678:90)
|
|
|
|
* new Bucket (/path/to/bucket.ts:987:65)
|
|
|
|
* <user code> (/path/to/index.ts:4:3)
|
|
|
|
*
|
|
|
|
* Because Node can only give us the stack trace as text, we parse out the source position using a regex that
|
|
|
|
* matches traces of this form (see stackTraceRegExp above).
|
|
|
|
*/
|
2023-07-10 17:59:15 +00:00
|
|
|
private static sourcePosition(): SourcePosition | undefined {
|
|
|
|
const stackObj: any = {};
|
|
|
|
Error.captureStackTrace(stackObj, Resource.sourcePosition);
|
|
|
|
|
|
|
|
// Parse out the source position of the user code. If any part of the match is missing, return undefined.
|
|
|
|
const { file, line, col } = Resource.sourcePositionRegExp.exec(stackObj.stack)?.groups || {};
|
|
|
|
if (!file || !line || !col) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the line and column numbers. If either fails to parse, return undefined.
|
|
|
|
//
|
|
|
|
// Note: this really shouldn't happen given the regex; this is just a bit of defensive coding.
|
|
|
|
const lineNum = parseInt(line, 10);
|
|
|
|
const colNum = parseInt(col, 10);
|
|
|
|
if (Number.isNaN(lineNum) || Number.isNaN(colNum)) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
uri: url.pathToFileURL(file).toString(),
|
|
|
|
line: lineNum,
|
|
|
|
column: colNum,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-06-03 12:50:03 +00:00
|
|
|
/**
|
|
|
|
* Returns the provider for the given module member, if one exists.
|
|
|
|
*/
|
2018-08-10 23:18:21 +00:00
|
|
|
public getProvider(moduleMember: string): ProviderResource | undefined {
|
2023-06-26 06:25:18 +00:00
|
|
|
const pkg = pkgFromType(moduleMember);
|
|
|
|
if (pkg === undefined) {
|
2018-08-10 23:18:21 +00:00
|
|
|
return undefined;
|
|
|
|
}
|
2022-01-25 19:11:41 +00:00
|
|
|
|
2018-08-10 23:18:21 +00:00
|
|
|
return this.__providers[pkg];
|
|
|
|
}
|
|
|
|
|
Implement components
This change implements core support for "components" in the Pulumi
Fabric. This work is described further in pulumi/pulumi#340, where
we are still discussing some of the finer points.
In a nutshell, resources no longer imply external providers. It's
entirely possible to have a resource that logically represents
something but without having a physical manifestation that needs to
be tracked and managed by our typical CRUD operations.
For example, the aws/serverless/Function helper is one such type.
It aggregates Lambda-related resources and exposes a nice interface.
All of the Pulumi Cloud Framework resources are also examples.
To indicate that a resource does participate in the usual CRUD resource
provider, it simply derives from ExternalResource instead of Resource.
All resources now have the ability to adopt children. This is purely
a metadata/tagging thing, and will help us roll up displays, provide
attribution to the developer, and even hide aspects of the resource
graph as appropriate (e.g., when they are implementation details).
Our use of this capability is ultra limited right now; in fact, the
only place we display children is in the CLI output. For instance:
+ aws:serverless:Function: (create)
[urn=urn:pulumi:demo::serverless::aws:serverless:Function::mylambda]
=> urn:pulumi:demo::serverless::aws:iam/role:Role::mylambda-iamrole
=> urn:pulumi:demo::serverless::aws:iam/rolePolicyAttachment:RolePolicyAttachment::mylambda-iampolicy-0
=> urn:pulumi:demo::serverless::aws:lambda/function:Function::mylambda
The bit indicating whether a resource is external or not is tracked
in the resulting checkpoint file, along with any of its children.
2017-10-14 21:18:43 +00:00
|
|
|
/**
|
2018-12-18 00:00:31 +00:00
|
|
|
* Creates and registers a new resource object. [t] 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.
|
2018-01-26 02:50:58 +00:00
|
|
|
* dependsOn is an optional list of other resources that this resource depends on, controlling
|
|
|
|
* the order in which we perform resource operations.
|
2017-09-22 01:15:29 +00:00
|
|
|
*
|
|
|
|
* @param t The type of the resource.
|
2018-02-07 23:01:55 +00:00
|
|
|
* @param name The _unique_ name of the resource.
|
2017-10-15 10:52:04 +00:00
|
|
|
* @param custom True to indicate that this is a custom resource, managed by a plugin.
|
2017-09-22 01:15:29 +00:00
|
|
|
* @param props The arguments to use to populate the new resource.
|
Implement resource protection (#751)
This change implements resource protection, as per pulumi/pulumi#689.
The overall idea is that a resource can be marked as "protect: true",
which will prevent deletion of that resource for any reason whatsoever
(straight deletion, replacement, etc). This is expressed in the
program. To "unprotect" a resource, one must perform an update setting
"protect: false", and then afterwards, they can delete the resource.
For example:
let res = new MyResource("precious", { .. }, { protect: true });
Afterwards, the resource will display in the CLI with a lock icon, and
any attempts to remove it will fail in the usual ways (in planning or,
worst case, during an actual update).
This was done by adding a new ResourceOptions bag parameter to the
base Resource types. This is unfortunately a breaking change, but now
is the right time to take this one. We had been adding new settings
one by one -- like parent and dependsOn -- and this new approach will
set us up to add any number of additional settings down the road,
without needing to worry about breaking anything ever again.
This is related to protected stacks, as described in
pulumi/pulumi-service#399. Most likely this will serve as a foundational
building block that enables the coarser grained policy management.
2017-12-20 22:31:07 +00:00
|
|
|
* @param opts A bag of options that control this resource's behavior.
|
Initial support for remote component construction. (#5280)
These changes add initial support for the construction of remote
components. For now, this support is limited to the NodeJS SDK;
follow-up changes will implement support for the other SDKs.
Remote components are component resources that are constructed and
managed by plugins rather than by Pulumi programs. In this sense, they
are a bit like cloud resources, and are supported by the same
distribution and plugin loading mechanisms and described by the same
schema system.
The construction of a remote component is initiated by a
`RegisterResourceRequest` with the new `remote` field set to `true`.
When the resource monitor receives such a request, it loads the plugin
that implements the component resource and calls the `Construct`
method added to the resource provider interface as part of these
changes. This method accepts the information necessary to construct the
component and its children: the component's name, type, resource
options, inputs, and input dependencies. It is responsible for
dispatching to the appropriate component factory to create the
component, then returning its URN, resolved output properties, and
output property dependencies. The dependency information is necessary to
support features such as delete-before-replace, which rely on precise
dependency information for custom resources.
These changes also add initial support for more conveniently
implementing resource providers in NodeJS. The interface used to
implement such a provider is similar to the dynamic provider interface
(and may be unified with that interface in the future).
An example of a NodeJS program constructing a remote component resource
also implemented in NodeJS can be found in
`tests/construct_component/nodejs`.
This is the core of #2430.
2020-09-08 02:33:55 +00:00
|
|
|
* @param remote True if this is a remote component resource.
|
|
|
|
* @param dependency True if this is a synthetic resource used internally for dependency tracking.
|
2017-09-22 01:15:29 +00:00
|
|
|
*/
|
2023-04-28 22:27:10 +00:00
|
|
|
constructor(
|
|
|
|
t: string,
|
|
|
|
name: string,
|
|
|
|
custom: boolean,
|
|
|
|
props: Inputs = {},
|
|
|
|
opts: ResourceOptions = {},
|
|
|
|
remote: boolean = false,
|
|
|
|
dependency: boolean = false,
|
|
|
|
) {
|
2023-06-23 21:32:03 +00:00
|
|
|
this.__pulumiType = t;
|
|
|
|
|
Initial support for remote component construction. (#5280)
These changes add initial support for the construction of remote
components. For now, this support is limited to the NodeJS SDK;
follow-up changes will implement support for the other SDKs.
Remote components are component resources that are constructed and
managed by plugins rather than by Pulumi programs. In this sense, they
are a bit like cloud resources, and are supported by the same
distribution and plugin loading mechanisms and described by the same
schema system.
The construction of a remote component is initiated by a
`RegisterResourceRequest` with the new `remote` field set to `true`.
When the resource monitor receives such a request, it loads the plugin
that implements the component resource and calls the `Construct`
method added to the resource provider interface as part of these
changes. This method accepts the information necessary to construct the
component and its children: the component's name, type, resource
options, inputs, and input dependencies. It is responsible for
dispatching to the appropriate component factory to create the
component, then returning its URN, resolved output properties, and
output property dependencies. The dependency information is necessary to
support features such as delete-before-replace, which rely on precise
dependency information for custom resources.
These changes also add initial support for more conveniently
implementing resource providers in NodeJS. The interface used to
implement such a provider is similar to the dynamic provider interface
(and may be unified with that interface in the future).
An example of a NodeJS program constructing a remote component resource
also implemented in NodeJS can be found in
`tests/construct_component/nodejs`.
This is the core of #2430.
2020-09-08 02:33:55 +00:00
|
|
|
if (dependency) {
|
|
|
|
this.__protect = false;
|
|
|
|
this.__providers = {};
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-13 19:21:33 +00:00
|
|
|
if (opts.parent && !Resource.isInstance(opts.parent)) {
|
|
|
|
throw new Error(`Resource parent is not a valid Resource: ${opts.parent}`);
|
|
|
|
}
|
|
|
|
|
Implement components
This change implements core support for "components" in the Pulumi
Fabric. This work is described further in pulumi/pulumi#340, where
we are still discussing some of the finer points.
In a nutshell, resources no longer imply external providers. It's
entirely possible to have a resource that logically represents
something but without having a physical manifestation that needs to
be tracked and managed by our typical CRUD operations.
For example, the aws/serverless/Function helper is one such type.
It aggregates Lambda-related resources and exposes a nice interface.
All of the Pulumi Cloud Framework resources are also examples.
To indicate that a resource does participate in the usual CRUD resource
provider, it simply derives from ExternalResource instead of Resource.
All resources now have the ability to adopt children. This is purely
a metadata/tagging thing, and will help us roll up displays, provide
attribution to the developer, and even hide aspects of the resource
graph as appropriate (e.g., when they are implementation details).
Our use of this capability is ultra limited right now; in fact, the
only place we display children is in the CLI output. For instance:
+ aws:serverless:Function: (create)
[urn=urn:pulumi:demo::serverless::aws:serverless:Function::mylambda]
=> urn:pulumi:demo::serverless::aws:iam/role:Role::mylambda-iamrole
=> urn:pulumi:demo::serverless::aws:iam/rolePolicyAttachment:RolePolicyAttachment::mylambda-iampolicy-0
=> urn:pulumi:demo::serverless::aws:lambda/function:Function::mylambda
The bit indicating whether a resource is external or not is tracked
in the resulting checkpoint file, along with any of its children.
2017-10-14 21:18:43 +00:00
|
|
|
if (!t) {
|
2018-09-24 23:57:20 +00:00
|
|
|
throw new ResourceError("Missing resource type argument", opts.parent);
|
Implement initial Lumi-as-a-library
This is the initial step towards redefining Lumi as a library that runs
atop vanilla Node.js/V8, rather than as its own runtime.
This change is woefully incomplete but this includes some of the more
stable pieces of my current work-in-progress.
The new structure is that within the sdk/ directory we will have a client
library per language. This client library contains the object model for
Lumi (resources, properties, assets, config, etc), in addition to the
"language runtime host" components required to interoperate with the
Lumi resource monitor. This resource monitor is effectively what we call
"Lumi" today, in that it's the thing orchestrating plans and deployments.
Inside the sdk/ directory, you will find nodejs/, the Node.js client
library, alongside proto/, the definitions for RPC interop between the
different pieces of the system. This includes existing RPC definitions
for resource providers, etc., in addition to the new ones for hosting
different language runtimes from within Lumi.
These new interfaces are surprisingly simple. There is effectively a
bidirectional RPC channel between the Lumi resource monitor, represented
by the lumirpc.ResourceMonitor interface, and each language runtime,
represented by the lumirpc.LanguageRuntime interface.
The overall orchestration goes as follows:
1) Lumi decides it needs to run a program written in language X, so
it dynamically loads the language runtime plugin for language X.
2) Lumi passes that runtime a loopback address to its ResourceMonitor
service, while language X will publish a connection back to its
LanguageRuntime service, which Lumi will talk to.
3) Lumi then invokes LanguageRuntime.Run, passing information like
the desired working directory, program name, arguments, and optional
configuration variables to make available to the program.
4) The language X runtime receives this, unpacks it and sets up the
necessary context, and then invokes the program. The program then
calls into Lumi object model abstractions that internally communicate
back to Lumi using the ResourceMonitor interface.
5) The key here is ResourceMonitor.NewResource, which Lumi uses to
serialize state about newly allocated resources. Lumi receives these
and registers them as part of the plan, doing the usual diffing, etc.,
to decide how to proceed. This interface is perhaps one of the
most subtle parts of the new design, as it necessitates the use of
promises internally to allow parallel evaluation of the resource plan,
letting dataflow determine the available concurrency.
6) The program exits, and Lumi continues on its merry way. If the program
fails, the RunResponse will include information about the failure.
Due to (5), all properties on resources are now instances of a new
Property<T> type. A Property<T> is just a thin wrapper over a T, but it
encodes the special properties of Lumi resource properties. Namely, it
is possible to create one out of a T, other Property<T>, Promise<T>, or
to freshly allocate one. In all cases, the Property<T> does not "settle"
until its final state is known. This cannot occur before the deployment
actually completes, and so in general it's not safe to depend on concrete
resolutions of values (unlike ordinary Promise<T>s which are usually
expected to resolve). As a result, all derived computations are meant to
use the `then` function (as in `someValue.then(v => v+x)`).
Although this change includes tests that may be run in isolation to test
the various RPC interactions, we are nowhere near finished. The remaining
work primarily boils down to three things:
1) Wiring all of this up to the Lumi code.
2) Fixing the handful of known loose ends required to make this work,
primarily around the serialization of properties (waiting on
unresolved ones, serializing assets properly, etc).
3) Implementing lambda closure serialization as a native extension.
This ongoing work is part of pulumi/pulumi-fabric#311.
2017-08-26 19:07:54 +00:00
|
|
|
}
|
Implement components
This change implements core support for "components" in the Pulumi
Fabric. This work is described further in pulumi/pulumi#340, where
we are still discussing some of the finer points.
In a nutshell, resources no longer imply external providers. It's
entirely possible to have a resource that logically represents
something but without having a physical manifestation that needs to
be tracked and managed by our typical CRUD operations.
For example, the aws/serverless/Function helper is one such type.
It aggregates Lambda-related resources and exposes a nice interface.
All of the Pulumi Cloud Framework resources are also examples.
To indicate that a resource does participate in the usual CRUD resource
provider, it simply derives from ExternalResource instead of Resource.
All resources now have the ability to adopt children. This is purely
a metadata/tagging thing, and will help us roll up displays, provide
attribution to the developer, and even hide aspects of the resource
graph as appropriate (e.g., when they are implementation details).
Our use of this capability is ultra limited right now; in fact, the
only place we display children is in the CLI output. For instance:
+ aws:serverless:Function: (create)
[urn=urn:pulumi:demo::serverless::aws:serverless:Function::mylambda]
=> urn:pulumi:demo::serverless::aws:iam/role:Role::mylambda-iamrole
=> urn:pulumi:demo::serverless::aws:iam/rolePolicyAttachment:RolePolicyAttachment::mylambda-iampolicy-0
=> urn:pulumi:demo::serverless::aws:lambda/function:Function::mylambda
The bit indicating whether a resource is external or not is tracked
in the resulting checkpoint file, along with any of its children.
2017-10-14 21:18:43 +00:00
|
|
|
if (!name) {
|
2018-09-24 23:57:20 +00:00
|
|
|
throw new ResourceError("Missing resource name argument (for URN creation)", opts.parent);
|
Implement initial Lumi-as-a-library
This is the initial step towards redefining Lumi as a library that runs
atop vanilla Node.js/V8, rather than as its own runtime.
This change is woefully incomplete but this includes some of the more
stable pieces of my current work-in-progress.
The new structure is that within the sdk/ directory we will have a client
library per language. This client library contains the object model for
Lumi (resources, properties, assets, config, etc), in addition to the
"language runtime host" components required to interoperate with the
Lumi resource monitor. This resource monitor is effectively what we call
"Lumi" today, in that it's the thing orchestrating plans and deployments.
Inside the sdk/ directory, you will find nodejs/, the Node.js client
library, alongside proto/, the definitions for RPC interop between the
different pieces of the system. This includes existing RPC definitions
for resource providers, etc., in addition to the new ones for hosting
different language runtimes from within Lumi.
These new interfaces are surprisingly simple. There is effectively a
bidirectional RPC channel between the Lumi resource monitor, represented
by the lumirpc.ResourceMonitor interface, and each language runtime,
represented by the lumirpc.LanguageRuntime interface.
The overall orchestration goes as follows:
1) Lumi decides it needs to run a program written in language X, so
it dynamically loads the language runtime plugin for language X.
2) Lumi passes that runtime a loopback address to its ResourceMonitor
service, while language X will publish a connection back to its
LanguageRuntime service, which Lumi will talk to.
3) Lumi then invokes LanguageRuntime.Run, passing information like
the desired working directory, program name, arguments, and optional
configuration variables to make available to the program.
4) The language X runtime receives this, unpacks it and sets up the
necessary context, and then invokes the program. The program then
calls into Lumi object model abstractions that internally communicate
back to Lumi using the ResourceMonitor interface.
5) The key here is ResourceMonitor.NewResource, which Lumi uses to
serialize state about newly allocated resources. Lumi receives these
and registers them as part of the plan, doing the usual diffing, etc.,
to decide how to proceed. This interface is perhaps one of the
most subtle parts of the new design, as it necessitates the use of
promises internally to allow parallel evaluation of the resource plan,
letting dataflow determine the available concurrency.
6) The program exits, and Lumi continues on its merry way. If the program
fails, the RunResponse will include information about the failure.
Due to (5), all properties on resources are now instances of a new
Property<T> type. A Property<T> is just a thin wrapper over a T, but it
encodes the special properties of Lumi resource properties. Namely, it
is possible to create one out of a T, other Property<T>, Promise<T>, or
to freshly allocate one. In all cases, the Property<T> does not "settle"
until its final state is known. This cannot occur before the deployment
actually completes, and so in general it's not safe to depend on concrete
resolutions of values (unlike ordinary Promise<T>s which are usually
expected to resolve). As a result, all derived computations are meant to
use the `then` function (as in `someValue.then(v => v+x)`).
Although this change includes tests that may be run in isolation to test
the various RPC interactions, we are nowhere near finished. The remaining
work primarily boils down to three things:
1) Wiring all of this up to the Lumi code.
2) Fixing the handful of known loose ends required to make this work,
primarily around the serialization of properties (waiting on
unresolved ones, serializing assets properly, etc).
3) Implementing lambda closure serialization as a native extension.
This ongoing work is part of pulumi/pulumi-fabric#311.
2017-08-26 19:07:54 +00:00
|
|
|
}
|
|
|
|
|
2019-10-15 00:35:00 +00:00
|
|
|
// Before anything else - if there are transformations registered, invoke them in order to transform the properties and
|
|
|
|
// options assigned to this resource.
|
2022-06-30 10:04:49 +00:00
|
|
|
const parent = opts.parent || getStackResource();
|
2023-04-28 22:27:10 +00:00
|
|
|
this.__transformations = [...(opts.transformations || []), ...(parent?.__transformations || [])];
|
2019-10-15 00:35:00 +00:00
|
|
|
for (const transformation of this.__transformations) {
|
|
|
|
const tres = transformation({ resource: this, type: t, name, props, opts });
|
|
|
|
if (tres) {
|
|
|
|
if (tres.opts.parent !== opts.parent) {
|
|
|
|
// This is currently not allowed because the parent tree is needed to establish what
|
|
|
|
// transformation to apply in the first place, and to compute inheritance of other
|
|
|
|
// resource options in the Resource constructor before transformations are run (so
|
|
|
|
// modifying it here would only even partially take affect). It's theoretically
|
|
|
|
// possible this restriction could be lifted in the future, but for now just
|
|
|
|
// disallow re-parenting resources in transformations to be safe.
|
|
|
|
throw new Error("Transformations cannot currently be used to change the `parent` of a resource.");
|
|
|
|
}
|
|
|
|
props = tres.props;
|
|
|
|
opts = tres.opts;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-01 06:01:01 +00:00
|
|
|
this.__name = name;
|
|
|
|
|
|
|
|
// Make a shallow clone of opts to ensure we don't modify the value passed in.
|
|
|
|
opts = Object.assign({}, opts);
|
|
|
|
|
2018-09-18 18:47:34 +00:00
|
|
|
// Check the parent type if one exists and fill in any default options.
|
2018-08-10 23:18:21 +00:00
|
|
|
this.__providers = {};
|
2022-06-30 10:04:49 +00:00
|
|
|
if (parent) {
|
|
|
|
this.__parentResource = parent;
|
2019-03-06 01:06:57 +00:00
|
|
|
this.__parentResource.__childResources = this.__parentResource.__childResources || new Set();
|
|
|
|
this.__parentResource.__childResources.add(this);
|
|
|
|
|
2018-08-10 23:18:21 +00:00
|
|
|
if (opts.protect === undefined) {
|
2022-06-30 10:04:49 +00:00
|
|
|
opts.protect = parent.__protect;
|
2018-08-10 23:18:21 +00:00
|
|
|
}
|
|
|
|
|
2022-06-30 10:04:49 +00:00
|
|
|
this.__providers = parent.__providers;
|
2019-06-11 23:57:37 +00:00
|
|
|
}
|
2018-09-07 16:42:19 +00:00
|
|
|
|
2022-01-25 19:11:41 +00:00
|
|
|
// providers is found by combining (in ascending order of priority)
|
|
|
|
// 1. provider
|
|
|
|
// 2. self_providers
|
|
|
|
// 3. opts.providers
|
|
|
|
this.__providers = {
|
|
|
|
...this.__providers,
|
|
|
|
...convertToProvidersMap((<ComponentResourceOptions>opts).providers),
|
|
|
|
...convertToProvidersMap(opts.provider ? [opts.provider] : {}),
|
|
|
|
};
|
|
|
|
|
[sdk/nodejs] Fix provider for resource methods (#13796)
The `Resource` class in the Node.js SDK has the following internal
property:
```typescript
/** @internal */
readonly __prov?: ProviderResource;
```
When a resource is created, the provider specified for the resource is
stored in this property. If it is set, it is passed along in the `Call`
request when a method is called on the resource.
Prior to #13282, the property was only set for custom resources in
`Resource`'s constructor:
```typescript
this.__prov = custom ? opts.provider : undefined;
```
With #13282, it was changed to also store the value for remote
components:
```diff
- this.__prov = custom ? opts.provider : undefined;
+ this.__prov = custom || remote ? opts.provider : undefined;
```
This regressed the behavior when calling a method on a remote component
that had an explicit provider that wasn't the component provider, but
some other provider (e.g. AWS provider) specified as:
```typescript
const component = new MyRemoteComponent("comp", {
}, { provider: awsProvider });
```
The `awsProvider` was being stored in `Resource.__prov`, and when making
the method call on the resource, it would try to invoke `Call` on the
AWS provider, rather than calling the remote component provider's
`Call`, which resulted in an error.
Note that specifying the AWS provider using the more verbose `providers:
[awsProvider]` works around the issue.
The fix is to only set `__prov` if the provider's package is the same as
the resource's package. Otherwise, don't set it, because the user is
specifying a provider with the `provider: awsProvider` syntax as
shorthand for `providers: [awsProvider]`.
Fixes #13777
2023-08-30 14:49:53 +00:00
|
|
|
const pkg = pkgFromType(t);
|
|
|
|
|
2022-01-25 19:11:41 +00:00
|
|
|
// provider is the first option that does not return none
|
|
|
|
// 1. opts.provider
|
|
|
|
// 2. a matching provider in opts.providers
|
|
|
|
// 3. a matching provider inherited from opts.parent
|
2023-06-26 06:25:18 +00:00
|
|
|
if ((custom || remote) && opts.provider === undefined) {
|
2022-06-30 10:04:49 +00:00
|
|
|
const parentProvider = parent?.getProvider(t);
|
2022-01-25 19:11:41 +00:00
|
|
|
|
|
|
|
if (pkg && pkg in this.__providers) {
|
|
|
|
opts.provider = this.__providers[pkg];
|
2023-04-28 22:27:10 +00:00
|
|
|
} else if (parentProvider) {
|
2022-01-25 19:11:41 +00:00
|
|
|
opts.provider = parentProvider;
|
2018-09-07 16:42:19 +00:00
|
|
|
}
|
2018-04-25 00:23:18 +00:00
|
|
|
}
|
2019-06-06 23:20:12 +00:00
|
|
|
|
[sdk/nodejs] Fix provider for resource methods (#13796)
The `Resource` class in the Node.js SDK has the following internal
property:
```typescript
/** @internal */
readonly __prov?: ProviderResource;
```
When a resource is created, the provider specified for the resource is
stored in this property. If it is set, it is passed along in the `Call`
request when a method is called on the resource.
Prior to #13282, the property was only set for custom resources in
`Resource`'s constructor:
```typescript
this.__prov = custom ? opts.provider : undefined;
```
With #13282, it was changed to also store the value for remote
components:
```diff
- this.__prov = custom ? opts.provider : undefined;
+ this.__prov = custom || remote ? opts.provider : undefined;
```
This regressed the behavior when calling a method on a remote component
that had an explicit provider that wasn't the component provider, but
some other provider (e.g. AWS provider) specified as:
```typescript
const component = new MyRemoteComponent("comp", {
}, { provider: awsProvider });
```
The `awsProvider` was being stored in `Resource.__prov`, and when making
the method call on the resource, it would try to invoke `Call` on the
AWS provider, rather than calling the remote component provider's
`Call`, which resulted in an error.
Note that specifying the AWS provider using the more verbose `providers:
[awsProvider]` works around the issue.
The fix is to only set `__prov` if the provider's package is the same as
the resource's package. Otherwise, don't set it, because the user is
specifying a provider with the `provider: awsProvider` syntax as
shorthand for `providers: [awsProvider]`.
Fixes #13777
2023-08-30 14:49:53 +00:00
|
|
|
// Custom and remote resources have a backing provider. If this is a custom or
|
|
|
|
// remote resource and a provider has been specified that has the same package
|
|
|
|
// as the resource's package, save it in `__prov`.
|
|
|
|
// If the provider's package isn't the same as the resource's package, don't
|
|
|
|
// save it in `__prov` because the user specified `provider: someProvider` as
|
|
|
|
// shorthand for `providers: [someProvider]`, which is a provider intended for
|
|
|
|
// the component's children and not for this resource itself.
|
|
|
|
// `__prov` is passed along in `Call` gRPC requests for resource method calls
|
|
|
|
// (when set) so that the call goes to the same provider as the resource.
|
|
|
|
if ((custom || remote) && opts.provider) {
|
|
|
|
if (pkg && pkg === opts.provider.getPackage()) {
|
|
|
|
this.__prov = opts.provider;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-10 23:18:21 +00:00
|
|
|
this.__protect = !!opts.protect;
|
2021-07-07 23:03:56 +00:00
|
|
|
this.__version = opts.version;
|
2022-01-10 23:54:41 +00:00
|
|
|
this.__pluginDownloadURL = opts.pluginDownloadURL;
|
2018-04-25 00:23:18 +00:00
|
|
|
|
2019-06-01 06:01:01 +00:00
|
|
|
// Collapse any `Alias`es down to URNs. We have to wait until this point to do so because we do not know the
|
|
|
|
// default `name` and `type` to apply until we are inside the resource constructor.
|
|
|
|
this.__aliases = [];
|
|
|
|
if (opts.aliases) {
|
|
|
|
for (const alias of opts.aliases) {
|
2022-06-30 15:13:04 +00:00
|
|
|
this.__aliases.push(collapseAliasToUrn(alias, name, t, parent));
|
2019-06-01 06:01:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-10 17:59:15 +00:00
|
|
|
const sourcePosition = Resource.sourcePosition();
|
|
|
|
|
2020-12-01 18:58:15 +00:00
|
|
|
if (opts.urn) {
|
|
|
|
// This is a resource that already exists. Read its state from the engine.
|
2022-06-30 10:04:49 +00:00
|
|
|
getResource(this, parent, props, custom, opts.urn);
|
2023-04-28 22:27:10 +00:00
|
|
|
} else if (opts.id) {
|
Initial support for remote component construction. (#5280)
These changes add initial support for the construction of remote
components. For now, this support is limited to the NodeJS SDK;
follow-up changes will implement support for the other SDKs.
Remote components are component resources that are constructed and
managed by plugins rather than by Pulumi programs. In this sense, they
are a bit like cloud resources, and are supported by the same
distribution and plugin loading mechanisms and described by the same
schema system.
The construction of a remote component is initiated by a
`RegisterResourceRequest` with the new `remote` field set to `true`.
When the resource monitor receives such a request, it loads the plugin
that implements the component resource and calls the `Construct`
method added to the resource provider interface as part of these
changes. This method accepts the information necessary to construct the
component and its children: the component's name, type, resource
options, inputs, and input dependencies. It is responsible for
dispatching to the appropriate component factory to create the
component, then returning its URN, resolved output properties, and
output property dependencies. The dependency information is necessary to
support features such as delete-before-replace, which rely on precise
dependency information for custom resources.
These changes also add initial support for more conveniently
implementing resource providers in NodeJS. The interface used to
implement such a provider is similar to the dynamic provider interface
(and may be unified with that interface in the future).
An example of a NodeJS program constructing a remote component resource
also implemented in NodeJS can be found in
`tests/construct_component/nodejs`.
This is the core of #2430.
2020-09-08 02:33:55 +00:00
|
|
|
// If this is a custom resource that already exists, read its state from the provider.
|
2018-04-05 16:48:09 +00:00
|
|
|
if (!custom) {
|
2018-09-24 23:57:20 +00:00
|
|
|
throw new ResourceError(
|
2023-04-28 22:27:10 +00:00
|
|
|
"Cannot read an existing resource unless it has a custom provider",
|
|
|
|
opts.parent,
|
|
|
|
);
|
2018-04-05 16:48:09 +00:00
|
|
|
}
|
2023-07-10 17:59:15 +00:00
|
|
|
readResource(this, parent, t, name, props, opts, sourcePosition);
|
2018-04-05 16:48:09 +00:00
|
|
|
} else {
|
|
|
|
// Kick off the resource registration. If we are actually performing a deployment, this
|
|
|
|
// resource's properties will be resolved asynchronously after the operation completes, so
|
|
|
|
// that dependent computations resolve normally. If we are just planning, on the other
|
|
|
|
// hand, values will never resolve.
|
2023-07-10 17:59:15 +00:00
|
|
|
registerResource(
|
|
|
|
this,
|
|
|
|
parent,
|
|
|
|
t,
|
|
|
|
name,
|
|
|
|
custom,
|
|
|
|
remote,
|
|
|
|
(urn) => new DependencyResource(urn),
|
|
|
|
props,
|
|
|
|
opts,
|
|
|
|
sourcePosition,
|
|
|
|
);
|
2018-04-05 16:48:09 +00:00
|
|
|
}
|
Implement components
This change implements core support for "components" in the Pulumi
Fabric. This work is described further in pulumi/pulumi#340, where
we are still discussing some of the finer points.
In a nutshell, resources no longer imply external providers. It's
entirely possible to have a resource that logically represents
something but without having a physical manifestation that needs to
be tracked and managed by our typical CRUD operations.
For example, the aws/serverless/Function helper is one such type.
It aggregates Lambda-related resources and exposes a nice interface.
All of the Pulumi Cloud Framework resources are also examples.
To indicate that a resource does participate in the usual CRUD resource
provider, it simply derives from ExternalResource instead of Resource.
All resources now have the ability to adopt children. This is purely
a metadata/tagging thing, and will help us roll up displays, provide
attribution to the developer, and even hide aspects of the resource
graph as appropriate (e.g., when they are implementation details).
Our use of this capability is ultra limited right now; in fact, the
only place we display children is in the CLI output. For instance:
+ aws:serverless:Function: (create)
[urn=urn:pulumi:demo::serverless::aws:serverless:Function::mylambda]
=> urn:pulumi:demo::serverless::aws:iam/role:Role::mylambda-iamrole
=> urn:pulumi:demo::serverless::aws:iam/rolePolicyAttachment:RolePolicyAttachment::mylambda-iampolicy-0
=> urn:pulumi:demo::serverless::aws:lambda/function:Function::mylambda
The bit indicating whether a resource is external or not is tracked
in the resulting checkpoint file, along with any of its children.
2017-10-14 21:18:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-06 23:20:12 +00:00
|
|
|
function convertToProvidersMap(providers: Record<string, ProviderResource> | ProviderResource[] | undefined) {
|
|
|
|
if (!providers) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Array.isArray(providers)) {
|
|
|
|
return providers;
|
|
|
|
}
|
|
|
|
|
|
|
|
const result: Record<string, ProviderResource> = {};
|
|
|
|
for (const provider of providers) {
|
|
|
|
result[provider.getPackage()] = provider;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-03-11 08:11:53 +00:00
|
|
|
(<any>Resource).doNotCapture = true;
|
|
|
|
|
2019-06-01 06:01:01 +00:00
|
|
|
/**
|
2019-06-20 22:53:33 +00:00
|
|
|
* Constant to represent the 'root stack' resource for a Pulumi application. The purpose of this is
|
|
|
|
* solely to make it easy to write an [Alias] like so:
|
|
|
|
*
|
|
|
|
* `aliases: [{ parent: rootStackResource }]`.
|
|
|
|
*
|
|
|
|
* This indicates that the prior name for a resource was created based on it being parented directly
|
|
|
|
* by the stack itself and no other resources. Note: this is equivalent to:
|
|
|
|
*
|
|
|
|
* `aliases: [{ parent: undefined }]`
|
|
|
|
*
|
|
|
|
* However, the former form is preferable as it is more self-descriptive, while the latter may look
|
|
|
|
* a bit confusing and may incorrectly look like something that could be removed without changing
|
|
|
|
* semantics.
|
|
|
|
*/
|
|
|
|
export const rootStackResource: Resource = undefined!;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Alias is a partial description of prior named used for a resource. It can be processed in the
|
|
|
|
* context of a resource creation to determine what the full aliased URN would be.
|
|
|
|
*
|
|
|
|
* Note there is a semantic difference between properties being absent from this type and properties
|
|
|
|
* having the `undefined` value. Specifically, there is a difference between:
|
|
|
|
*
|
|
|
|
* ```ts
|
|
|
|
* { name: "foo", parent: undefined } // and
|
|
|
|
* { name: "foo" }
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* The presence of a property indicates if its value should be used. If absent, then the value is
|
|
|
|
* not used. So, in the above while `alias.parent` is `undefined` for both, the first alias means
|
|
|
|
* "the original urn had no parent" while the second alias means "use the current parent".
|
|
|
|
*
|
|
|
|
* Note: to indicate that a resource was previously parented by the root stack, it is recommended
|
|
|
|
* that you use:
|
|
|
|
*
|
|
|
|
* `aliases: [{ parent: pulumi.rootStackResource }]`
|
|
|
|
*
|
|
|
|
* This form is self-descriptive and makes the intent clearer than using:
|
|
|
|
*
|
|
|
|
* `aliases: [{ parent: undefined }]`
|
2019-06-01 06:01:01 +00:00
|
|
|
*/
|
|
|
|
export interface Alias {
|
|
|
|
/**
|
2019-06-20 22:53:33 +00:00
|
|
|
* The previous name of the resource. If not provided, the current name of the resource is
|
|
|
|
* used.
|
2019-06-01 06:01:01 +00:00
|
|
|
*/
|
|
|
|
name?: Input<string>;
|
|
|
|
/**
|
|
|
|
* The previous type of the resource. If not provided, the current type of the resource is used.
|
|
|
|
*/
|
|
|
|
type?: Input<string>;
|
2019-06-20 22:53:33 +00:00
|
|
|
|
2019-06-01 06:01:01 +00:00
|
|
|
/**
|
2019-06-20 22:53:33 +00:00
|
|
|
* The previous parent of the resource. If not provided (i.e. `{ name: "foo" }`), the current
|
|
|
|
* parent of the resource is used (`opts.parent` if provided, else the implicit stack resource
|
|
|
|
* parent).
|
|
|
|
*
|
|
|
|
* To specify no original parent, use `{ parent: pulumi.rootStackResource }`.
|
2019-06-01 06:01:01 +00:00
|
|
|
*/
|
|
|
|
parent?: Resource | Input<URN>;
|
|
|
|
/**
|
|
|
|
* The previous stack of the resource. If not provided, defaults to `pulumi.getStack()`.
|
|
|
|
*/
|
|
|
|
stack?: Input<string>;
|
|
|
|
/**
|
|
|
|
* The previous project of the resource. If not provided, defaults to `pulumi.getProject()`.
|
|
|
|
*/
|
|
|
|
project?: Input<string>;
|
|
|
|
}
|
|
|
|
|
2024-06-03 12:50:03 +00:00
|
|
|
/**
|
|
|
|
* Converts an alias into a URN given a set of default data for the missing
|
|
|
|
* values.
|
|
|
|
*/
|
2019-06-01 06:01:01 +00:00
|
|
|
function collapseAliasToUrn(
|
2021-08-10 18:31:59 +00:00
|
|
|
alias: Input<Alias | string>,
|
|
|
|
defaultName: string,
|
|
|
|
defaultType: string,
|
2023-04-28 22:27:10 +00:00
|
|
|
defaultParent: Resource | undefined,
|
|
|
|
): Output<URN> {
|
|
|
|
return output(alias).apply((a) => {
|
2019-06-01 06:01:01 +00:00
|
|
|
if (typeof a === "string") {
|
|
|
|
return output(a);
|
|
|
|
}
|
2019-06-20 22:53:33 +00:00
|
|
|
|
|
|
|
const name = a.hasOwnProperty("name") ? a.name : defaultName;
|
|
|
|
const type = a.hasOwnProperty("type") ? a.type : defaultType;
|
|
|
|
const parent = a.hasOwnProperty("parent") ? a.parent : defaultParent;
|
|
|
|
const project = a.hasOwnProperty("project") ? a.project : getProject();
|
|
|
|
const stack = a.hasOwnProperty("stack") ? a.stack : getStack();
|
|
|
|
|
|
|
|
if (name === undefined) {
|
|
|
|
throw new Error("No valid 'name' passed in for alias.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type === undefined) {
|
|
|
|
throw new Error("No valid 'type' passed in for alias.");
|
|
|
|
}
|
|
|
|
|
|
|
|
return createUrn(name, type, parent, project, stack);
|
2019-06-01 06:01:01 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
Implement resource protection (#751)
This change implements resource protection, as per pulumi/pulumi#689.
The overall idea is that a resource can be marked as "protect: true",
which will prevent deletion of that resource for any reason whatsoever
(straight deletion, replacement, etc). This is expressed in the
program. To "unprotect" a resource, one must perform an update setting
"protect: false", and then afterwards, they can delete the resource.
For example:
let res = new MyResource("precious", { .. }, { protect: true });
Afterwards, the resource will display in the CLI with a lock icon, and
any attempts to remove it will fail in the usual ways (in planning or,
worst case, during an actual update).
This was done by adding a new ResourceOptions bag parameter to the
base Resource types. This is unfortunately a breaking change, but now
is the right time to take this one. We had been adding new settings
one by one -- like parent and dependsOn -- and this new approach will
set us up to add any number of additional settings down the road,
without needing to worry about breaking anything ever again.
This is related to protected stacks, as described in
pulumi/pulumi-service#399. Most likely this will serve as a foundational
building block that enables the coarser grained policy management.
2017-12-20 22:31:07 +00:00
|
|
|
/**
|
|
|
|
* ResourceOptions is a bag of optional settings that control a resource's behavior.
|
|
|
|
*/
|
|
|
|
export interface ResourceOptions {
|
2024-03-07 08:52:34 +00:00
|
|
|
// !!! IMPORTANT !!! If you add a new field to this type, make sure to add test that verifies that
|
|
|
|
// mergeOptions works properly for it. Also be sure to update the logic in callbacks.ts that marshals to
|
|
|
|
// and from this type to the wire protocol.
|
2019-07-29 19:01:10 +00:00
|
|
|
|
2018-04-07 17:15:58 +00:00
|
|
|
/**
|
|
|
|
* An optional existing ID to load, rather than create.
|
|
|
|
*/
|
|
|
|
id?: Input<ID>;
|
Implement resource protection (#751)
This change implements resource protection, as per pulumi/pulumi#689.
The overall idea is that a resource can be marked as "protect: true",
which will prevent deletion of that resource for any reason whatsoever
(straight deletion, replacement, etc). This is expressed in the
program. To "unprotect" a resource, one must perform an update setting
"protect: false", and then afterwards, they can delete the resource.
For example:
let res = new MyResource("precious", { .. }, { protect: true });
Afterwards, the resource will display in the CLI with a lock icon, and
any attempts to remove it will fail in the usual ways (in planning or,
worst case, during an actual update).
This was done by adding a new ResourceOptions bag parameter to the
base Resource types. This is unfortunately a breaking change, but now
is the right time to take this one. We had been adding new settings
one by one -- like parent and dependsOn -- and this new approach will
set us up to add any number of additional settings down the road,
without needing to worry about breaking anything ever again.
This is related to protected stacks, as described in
pulumi/pulumi-service#399. Most likely this will serve as a foundational
building block that enables the coarser grained policy management.
2017-12-20 22:31:07 +00:00
|
|
|
/**
|
|
|
|
* An optional parent resource to which this resource belongs.
|
|
|
|
*/
|
|
|
|
parent?: Resource;
|
|
|
|
/**
|
|
|
|
* An optional additional explicit dependencies on other resources.
|
|
|
|
*/
|
2018-11-19 16:22:55 +00:00
|
|
|
dependsOn?: Input<Input<Resource>[]> | Input<Resource>;
|
Implement resource protection (#751)
This change implements resource protection, as per pulumi/pulumi#689.
The overall idea is that a resource can be marked as "protect: true",
which will prevent deletion of that resource for any reason whatsoever
(straight deletion, replacement, etc). This is expressed in the
program. To "unprotect" a resource, one must perform an update setting
"protect: false", and then afterwards, they can delete the resource.
For example:
let res = new MyResource("precious", { .. }, { protect: true });
Afterwards, the resource will display in the CLI with a lock icon, and
any attempts to remove it will fail in the usual ways (in planning or,
worst case, during an actual update).
This was done by adding a new ResourceOptions bag parameter to the
base Resource types. This is unfortunately a breaking change, but now
is the right time to take this one. We had been adding new settings
one by one -- like parent and dependsOn -- and this new approach will
set us up to add any number of additional settings down the road,
without needing to worry about breaking anything ever again.
This is related to protected stacks, as described in
pulumi/pulumi-service#399. Most likely this will serve as a foundational
building block that enables the coarser grained policy management.
2017-12-20 22:31:07 +00:00
|
|
|
/**
|
|
|
|
* When set to true, protect ensures this resource cannot be deleted.
|
|
|
|
*/
|
|
|
|
protect?: boolean;
|
2019-04-22 20:54:48 +00:00
|
|
|
/**
|
|
|
|
* Ignore changes to any of the specified properties.
|
|
|
|
*/
|
|
|
|
ignoreChanges?: string[];
|
2021-07-01 19:32:08 +00:00
|
|
|
/**
|
|
|
|
* Changes to any of these property paths will force a replacement. If this list includes `"*"`, changes to any
|
|
|
|
* properties will force a replacement. Initialization errors from previous deployments will require replacement
|
|
|
|
* instead of update only if `"*"` is passed.
|
|
|
|
*/
|
|
|
|
replaceOnChanges?: string[];
|
2019-04-23 18:02:51 +00:00
|
|
|
/**
|
|
|
|
* An optional version, corresponding to the version of the provider plugin that should be used when operating on
|
|
|
|
* this resource. This version overrides the version information inferred from the current package and should
|
|
|
|
* rarely be used.
|
|
|
|
*/
|
|
|
|
version?: string;
|
2019-06-01 06:01:01 +00:00
|
|
|
/**
|
2019-06-06 23:20:12 +00:00
|
|
|
* An optional list of aliases to treat this resource as matching.
|
2019-06-01 06:01:01 +00:00
|
|
|
*/
|
|
|
|
aliases?: Input<URN | Alias>[];
|
2019-06-06 23:20:12 +00:00
|
|
|
/**
|
|
|
|
* An optional provider to use for this resource's CRUD operations. If no provider is supplied,
|
|
|
|
* the default provider for the resource's package will be used. The default provider is pulled
|
|
|
|
* from the parent's provider bag (see also ComponentResourceOptions.providers).
|
|
|
|
*
|
|
|
|
* If this is a [ComponentResourceOptions] do not provide both [provider] and [providers]
|
|
|
|
*/
|
|
|
|
provider?: ProviderResource;
|
Addition of Custom Timeouts (#2885)
* Plumbing the custom timeouts from the engine to the providers
* Plumbing the CustomTimeouts through to the engine and adding test to show this
* Change the provider proto to include individual timeouts
* Plumbing the CustomTimeouts from the engine through to the Provider RPC interface
* Change how the CustomTimeouts are sent across RPC
These errors were spotted in testing. We can now see that the timeout
information is arriving in the RegisterResourceRequest
```
req=&pulumirpc.RegisterResourceRequest{
Type: "aws:s3/bucket:Bucket",
Name: "my-bucket",
Parent: "urn:pulumi:dev::aws-vpc::pulumi:pulumi:Stack::aws-vpc-dev",
Custom: true,
Object: &structpb.Struct{},
Protect: false,
Dependencies: nil,
Provider: "",
PropertyDependencies: {},
DeleteBeforeReplace: false,
Version: "",
IgnoreChanges: nil,
AcceptSecrets: true,
AdditionalSecretOutputs: nil,
Aliases: nil,
CustomTimeouts: &pulumirpc.RegisterResourceRequest_CustomTimeouts{
Create: 300,
Update: 400,
Delete: 500,
XXX_NoUnkeyedLiteral: struct {}{},
XXX_unrecognized: nil,
XXX_sizecache: 0,
},
XXX_NoUnkeyedLiteral: struct {}{},
XXX_unrecognized: nil,
XXX_sizecache: 0,
}
```
* Changing the design to use strings
* CHANGELOG entry to include the CustomTimeouts work
* Changing custom timeouts to be passed around the engine as converted value
We don't want to pass around strings - the user can provide it but we want
to make the engine aware of the timeout in seconds as a float64
2019-07-15 21:26:28 +00:00
|
|
|
/**
|
|
|
|
* An optional customTimeouts configuration block.
|
|
|
|
*/
|
|
|
|
customTimeouts?: CustomTimeouts;
|
Transformations (#3174)
Adds the ability to provide `transformations` to modify the properties and resource options that will be used for any child resource of a component or stack.
This offers an "escape hatch" to modify the behaviour of a component by peeking behind it's abstraction. For example, it can be used to add a resource option (`additionalSecretOutputs`, `aliases`, `protect`, etc.) to a specific known child of a component, or to modify some input property to a child resource if the component does not (yet) expose the ability to control that input directly. It could also be used for more interesting scenarios - such as:
1. Automatically applying tags to all resources that support them in a stack (or component)
2. Injecting real dependencies between stringly-referenced resources in a Helm Chart
3. Injecting explicit names using a preferred naming convention across all resources in a stack
4. Injecting `import` onto all resources by doing a lookup into a name=>id mapping
Because this feature makes it possible to peek behind a component abstraction, it must be used with care in cases where the component is versioned independently of the use of transformations. Also, this can result in "spooky action at a distance", so should be used judiciously. That said - this can be used as an escape hatch to unblock a wide variety of common use cases without waiting on changes to be made in a component implementation.
Each transformation is passed the `resource`, `name`, `type`, `props` and `opts` that are passed into the `Resource` constructor for any resource descended from the resource that has the transformation applied. The transformation callback can optionally return alternate versions of the `props` and `opts` to be used in place of the original values provided to the resource constructor.
Fixes #2068.
2019-09-29 18:27:37 +00:00
|
|
|
/**
|
|
|
|
* Optional list of transformations to apply to this resource during construction. The
|
|
|
|
* transformations are applied in order, and are applied prior to transformation applied to
|
|
|
|
* parents walking from the resource up to the stack.
|
|
|
|
*/
|
|
|
|
transformations?: ResourceTransformation[];
|
2024-03-07 08:52:34 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Optional list of transforms to apply to this resource during construction. The
|
|
|
|
* transforms are applied in order, and are applied prior to transforms applied to
|
|
|
|
* parents walking from the resource up to the stack.
|
|
|
|
*
|
|
|
|
* This property is experimental.
|
|
|
|
*/
|
2024-04-29 10:54:41 +00:00
|
|
|
transforms?: ResourceTransform[];
|
2024-03-07 08:52:34 +00:00
|
|
|
|
2020-12-01 18:58:15 +00:00
|
|
|
/**
|
|
|
|
* The URN of a previously-registered resource of this type to read from the engine.
|
|
|
|
*/
|
|
|
|
urn?: URN;
|
2022-01-10 23:54:41 +00:00
|
|
|
/**
|
|
|
|
* An option to specify the URL from which to download this resources
|
|
|
|
* associated plugin. This version overrides the URL information inferred
|
|
|
|
* from the current package and should rarely be used.
|
|
|
|
*/
|
|
|
|
pluginDownloadURL?: string;
|
2022-02-16 22:11:12 +00:00
|
|
|
/**
|
|
|
|
* If set to True, the providers Delete method will not be called for this resource.
|
|
|
|
*/
|
|
|
|
retainOnDelete?: boolean;
|
2022-10-20 06:15:43 +00:00
|
|
|
/**
|
|
|
|
* If set, the providers Delete method will not be called for this resource
|
|
|
|
* if specified is being deleted as well.
|
|
|
|
*/
|
|
|
|
deletedWith?: Resource;
|
2019-07-29 19:01:10 +00:00
|
|
|
|
|
|
|
// !!! IMPORTANT !!! If you add a new field to this type, make sure to add test that verifies
|
|
|
|
// that mergeOptions works properly for it.
|
Addition of Custom Timeouts (#2885)
* Plumbing the custom timeouts from the engine to the providers
* Plumbing the CustomTimeouts through to the engine and adding test to show this
* Change the provider proto to include individual timeouts
* Plumbing the CustomTimeouts from the engine through to the Provider RPC interface
* Change how the CustomTimeouts are sent across RPC
These errors were spotted in testing. We can now see that the timeout
information is arriving in the RegisterResourceRequest
```
req=&pulumirpc.RegisterResourceRequest{
Type: "aws:s3/bucket:Bucket",
Name: "my-bucket",
Parent: "urn:pulumi:dev::aws-vpc::pulumi:pulumi:Stack::aws-vpc-dev",
Custom: true,
Object: &structpb.Struct{},
Protect: false,
Dependencies: nil,
Provider: "",
PropertyDependencies: {},
DeleteBeforeReplace: false,
Version: "",
IgnoreChanges: nil,
AcceptSecrets: true,
AdditionalSecretOutputs: nil,
Aliases: nil,
CustomTimeouts: &pulumirpc.RegisterResourceRequest_CustomTimeouts{
Create: 300,
Update: 400,
Delete: 500,
XXX_NoUnkeyedLiteral: struct {}{},
XXX_unrecognized: nil,
XXX_sizecache: 0,
},
XXX_NoUnkeyedLiteral: struct {}{},
XXX_unrecognized: nil,
XXX_sizecache: 0,
}
```
* Changing the design to use strings
* CHANGELOG entry to include the CustomTimeouts work
* Changing custom timeouts to be passed around the engine as converted value
We don't want to pass around strings - the user can provide it but we want
to make the engine aware of the timeout in seconds as a float64
2019-07-15 21:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface CustomTimeouts {
|
|
|
|
/**
|
|
|
|
* The optional create timeout represented as a string e.g. 5m, 40s, 1d.
|
|
|
|
*/
|
|
|
|
create?: string;
|
|
|
|
/**
|
|
|
|
* The optional update timeout represented as a string e.g. 5m, 40s, 1d.
|
|
|
|
*/
|
|
|
|
update?: string;
|
|
|
|
/**
|
|
|
|
* The optional delete timeout represented as a string e.g. 5m, 40s, 1d.
|
|
|
|
*/
|
|
|
|
delete?: string;
|
2018-08-10 23:18:21 +00:00
|
|
|
}
|
|
|
|
|
Transformations (#3174)
Adds the ability to provide `transformations` to modify the properties and resource options that will be used for any child resource of a component or stack.
This offers an "escape hatch" to modify the behaviour of a component by peeking behind it's abstraction. For example, it can be used to add a resource option (`additionalSecretOutputs`, `aliases`, `protect`, etc.) to a specific known child of a component, or to modify some input property to a child resource if the component does not (yet) expose the ability to control that input directly. It could also be used for more interesting scenarios - such as:
1. Automatically applying tags to all resources that support them in a stack (or component)
2. Injecting real dependencies between stringly-referenced resources in a Helm Chart
3. Injecting explicit names using a preferred naming convention across all resources in a stack
4. Injecting `import` onto all resources by doing a lookup into a name=>id mapping
Because this feature makes it possible to peek behind a component abstraction, it must be used with care in cases where the component is versioned independently of the use of transformations. Also, this can result in "spooky action at a distance", so should be used judiciously. That said - this can be used as an escape hatch to unblock a wide variety of common use cases without waiting on changes to be made in a component implementation.
Each transformation is passed the `resource`, `name`, `type`, `props` and `opts` that are passed into the `Resource` constructor for any resource descended from the resource that has the transformation applied. The transformation callback can optionally return alternate versions of the `props` and `opts` to be used in place of the original values provided to the resource constructor.
Fixes #2068.
2019-09-29 18:27:37 +00:00
|
|
|
/**
|
|
|
|
* ResourceTransformation is the callback signature for the `transformations` resource option. A
|
|
|
|
* transformation is passed the same set of inputs provided to the `Resource` constructor, and can
|
|
|
|
* optionally return back alternate values for the `props` and/or `opts` prior to the resource
|
|
|
|
* actually being created. The effect will be as though those props and opts were passed in place
|
|
|
|
* of the original call to the `Resource` constructor. If the transformation returns undefined,
|
|
|
|
* this indicates that the resource will not be transformed.
|
|
|
|
*/
|
|
|
|
export type ResourceTransformation = (args: ResourceTransformationArgs) => ResourceTransformationResult | undefined;
|
|
|
|
|
2024-03-07 08:52:34 +00:00
|
|
|
/**
|
|
|
|
* ResourceTransform is the callback signature for the `transforms` resource option. A
|
|
|
|
* transform is passed the same set of inputs provided to the `Resource` constructor, and can
|
|
|
|
* optionally return back alternate values for the `props` and/or `opts` prior to the resource
|
|
|
|
* actually being created. The effect will be as though those props and opts were passed in place
|
|
|
|
* of the original call to the `Resource` constructor. If the transform returns undefined,
|
|
|
|
* this indicates that the resource will not be transformed.
|
|
|
|
*/
|
|
|
|
export type ResourceTransform = (
|
|
|
|
args: ResourceTransformArgs,
|
|
|
|
) => Promise<ResourceTransformResult | undefined> | ResourceTransformResult | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ResourceTransformArgs is the argument bag passed to a resource transform.
|
|
|
|
*/
|
|
|
|
export interface ResourceTransformArgs {
|
|
|
|
/**
|
|
|
|
* If the resource is a custom or component resource.
|
|
|
|
*/
|
|
|
|
custom: boolean;
|
|
|
|
/**
|
|
|
|
* The type of the Resource.
|
|
|
|
*/
|
|
|
|
type: string;
|
|
|
|
/**
|
|
|
|
* The name of the Resource.
|
|
|
|
*/
|
|
|
|
name: string;
|
|
|
|
/**
|
|
|
|
* The original properties passed to the Resource constructor.
|
|
|
|
*/
|
|
|
|
props: Inputs;
|
|
|
|
/**
|
|
|
|
* The original resource options passed to the Resource constructor.
|
|
|
|
*/
|
|
|
|
opts: ResourceOptions;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ResourceTransformResult is the result that must be returned by a resource transformation
|
|
|
|
* callback. It includes new values to use for the `props` and `opts` of the `Resource` in place of
|
|
|
|
* the originally provided values.
|
|
|
|
*/
|
|
|
|
export interface ResourceTransformResult {
|
|
|
|
/**
|
|
|
|
* The new properties to use in place of the original `props`
|
|
|
|
*/
|
|
|
|
props: Inputs;
|
|
|
|
/**
|
|
|
|
* The new resource options to use in place of the original `opts`
|
|
|
|
*/
|
|
|
|
opts: ResourceOptions;
|
|
|
|
}
|
|
|
|
|
Transformations (#3174)
Adds the ability to provide `transformations` to modify the properties and resource options that will be used for any child resource of a component or stack.
This offers an "escape hatch" to modify the behaviour of a component by peeking behind it's abstraction. For example, it can be used to add a resource option (`additionalSecretOutputs`, `aliases`, `protect`, etc.) to a specific known child of a component, or to modify some input property to a child resource if the component does not (yet) expose the ability to control that input directly. It could also be used for more interesting scenarios - such as:
1. Automatically applying tags to all resources that support them in a stack (or component)
2. Injecting real dependencies between stringly-referenced resources in a Helm Chart
3. Injecting explicit names using a preferred naming convention across all resources in a stack
4. Injecting `import` onto all resources by doing a lookup into a name=>id mapping
Because this feature makes it possible to peek behind a component abstraction, it must be used with care in cases where the component is versioned independently of the use of transformations. Also, this can result in "spooky action at a distance", so should be used judiciously. That said - this can be used as an escape hatch to unblock a wide variety of common use cases without waiting on changes to be made in a component implementation.
Each transformation is passed the `resource`, `name`, `type`, `props` and `opts` that are passed into the `Resource` constructor for any resource descended from the resource that has the transformation applied. The transformation callback can optionally return alternate versions of the `props` and `opts` to be used in place of the original values provided to the resource constructor.
Fixes #2068.
2019-09-29 18:27:37 +00:00
|
|
|
/**
|
|
|
|
* ResourceTransformationArgs is the argument bag passed to a resource transformation.
|
|
|
|
*/
|
|
|
|
export interface ResourceTransformationArgs {
|
|
|
|
/**
|
|
|
|
* The Resource instance that is being transformed.
|
|
|
|
*/
|
|
|
|
resource: Resource;
|
|
|
|
/**
|
|
|
|
* The type of the Resource.
|
|
|
|
*/
|
|
|
|
type: string;
|
|
|
|
/**
|
|
|
|
* The name of the Resource.
|
|
|
|
*/
|
|
|
|
name: string;
|
|
|
|
/**
|
|
|
|
* The original properties passed to the Resource constructor.
|
|
|
|
*/
|
|
|
|
props: Inputs;
|
|
|
|
/**
|
|
|
|
* The original resource options passed to the Resource constructor.
|
|
|
|
*/
|
|
|
|
opts: ResourceOptions;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ResourceTransformationResult is the result that must be returned by a resource transformation
|
|
|
|
* callback. It includes new values to use for the `props` and `opts` of the `Resource` in place of
|
|
|
|
* the originally provided values.
|
|
|
|
*/
|
|
|
|
export interface ResourceTransformationResult {
|
|
|
|
/**
|
|
|
|
* The new properties to use in place of the original `props`
|
|
|
|
*/
|
|
|
|
props: Inputs;
|
|
|
|
/**
|
|
|
|
* The new resource options to use in place of the original `opts`
|
|
|
|
*/
|
|
|
|
opts: ResourceOptions;
|
|
|
|
}
|
|
|
|
|
2018-08-10 23:18:21 +00:00
|
|
|
/**
|
|
|
|
* CustomResourceOptions is a bag of optional settings that control a custom resource's behavior.
|
|
|
|
*/
|
|
|
|
export interface CustomResourceOptions extends ResourceOptions {
|
2019-07-29 19:01:10 +00:00
|
|
|
// !!! IMPORTANT !!! If you add a new field to this type, make sure to add test that verifies
|
|
|
|
// that mergeOptions works properly for it.
|
|
|
|
|
2019-01-31 22:27:53 +00:00
|
|
|
/**
|
|
|
|
* When set to true, deleteBeforeReplace indicates that this resource should be deleted before its replacement
|
|
|
|
* is created when replacement is necessary.
|
|
|
|
*/
|
|
|
|
deleteBeforeReplace?: boolean;
|
2019-04-23 17:15:17 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The names of outputs for this resource that should be treated as secrets. This augments the list that
|
|
|
|
* the resource provider and pulumi engine already determine based on inputs to your resource. It can be used
|
|
|
|
* to mark certain ouputs as a secrets on a per resource basis.
|
|
|
|
*/
|
|
|
|
additionalSecretOutputs?: string[];
|
2019-07-12 18:12:01 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* When provided with a resource ID, import indicates that this resource's provider should import its state from
|
|
|
|
* the cloud resource with the given ID. The inputs to the resource's constructor must align with the resource's
|
|
|
|
* current state. Once a resource has been imported, the import property must be removed from the resource's
|
|
|
|
* options.
|
|
|
|
*/
|
|
|
|
import?: ID;
|
2019-07-29 19:01:10 +00:00
|
|
|
|
|
|
|
// !!! IMPORTANT !!! If you add a new field to this type, make sure to add test that verifies
|
|
|
|
// that mergeOptions works properly for it.
|
Implement resource protection (#751)
This change implements resource protection, as per pulumi/pulumi#689.
The overall idea is that a resource can be marked as "protect: true",
which will prevent deletion of that resource for any reason whatsoever
(straight deletion, replacement, etc). This is expressed in the
program. To "unprotect" a resource, one must perform an update setting
"protect: false", and then afterwards, they can delete the resource.
For example:
let res = new MyResource("precious", { .. }, { protect: true });
Afterwards, the resource will display in the CLI with a lock icon, and
any attempts to remove it will fail in the usual ways (in planning or,
worst case, during an actual update).
This was done by adding a new ResourceOptions bag parameter to the
base Resource types. This is unfortunately a breaking change, but now
is the right time to take this one. We had been adding new settings
one by one -- like parent and dependsOn -- and this new approach will
set us up to add any number of additional settings down the road,
without needing to worry about breaking anything ever again.
This is related to protected stacks, as described in
pulumi/pulumi-service#399. Most likely this will serve as a foundational
building block that enables the coarser grained policy management.
2017-12-20 22:31:07 +00:00
|
|
|
}
|
|
|
|
|
2018-08-10 23:18:21 +00:00
|
|
|
/**
|
|
|
|
* ComponentResourceOptions is a bag of optional settings that control a component resource's behavior.
|
|
|
|
*/
|
|
|
|
export interface ComponentResourceOptions extends ResourceOptions {
|
2019-07-29 19:01:10 +00:00
|
|
|
// !!! IMPORTANT !!! If you add a new field to this type, make sure to add test that verifies
|
|
|
|
// that mergeOptions works properly for it.
|
|
|
|
|
2018-08-10 23:18:21 +00:00
|
|
|
/**
|
2019-06-06 23:20:12 +00:00
|
|
|
* An optional set of providers to use for child resources. Either keyed by package name (e.g.
|
|
|
|
* "aws"), or just provided as an array. In the latter case, the package name will be retrieved
|
|
|
|
* from the provider itself.
|
|
|
|
*
|
2022-01-25 19:11:41 +00:00
|
|
|
* Note: only a list should be used. Mapping keys are not respected.
|
2018-08-10 23:18:21 +00:00
|
|
|
*/
|
2019-06-06 23:20:12 +00:00
|
|
|
providers?: Record<string, ProviderResource> | ProviderResource[];
|
2019-07-29 19:01:10 +00:00
|
|
|
|
|
|
|
// !!! IMPORTANT !!! If you add a new field to this type, make sure to add test that verifies
|
|
|
|
// that mergeOptions works properly for it.
|
2018-08-10 23:18:21 +00:00
|
|
|
}
|
|
|
|
|
Implement components
This change implements core support for "components" in the Pulumi
Fabric. This work is described further in pulumi/pulumi#340, where
we are still discussing some of the finer points.
In a nutshell, resources no longer imply external providers. It's
entirely possible to have a resource that logically represents
something but without having a physical manifestation that needs to
be tracked and managed by our typical CRUD operations.
For example, the aws/serverless/Function helper is one such type.
It aggregates Lambda-related resources and exposes a nice interface.
All of the Pulumi Cloud Framework resources are also examples.
To indicate that a resource does participate in the usual CRUD resource
provider, it simply derives from ExternalResource instead of Resource.
All resources now have the ability to adopt children. This is purely
a metadata/tagging thing, and will help us roll up displays, provide
attribution to the developer, and even hide aspects of the resource
graph as appropriate (e.g., when they are implementation details).
Our use of this capability is ultra limited right now; in fact, the
only place we display children is in the CLI output. For instance:
+ aws:serverless:Function: (create)
[urn=urn:pulumi:demo::serverless::aws:serverless:Function::mylambda]
=> urn:pulumi:demo::serverless::aws:iam/role:Role::mylambda-iamrole
=> urn:pulumi:demo::serverless::aws:iam/rolePolicyAttachment:RolePolicyAttachment::mylambda-iampolicy-0
=> urn:pulumi:demo::serverless::aws:lambda/function:Function::mylambda
The bit indicating whether a resource is external or not is tracked
in the resulting checkpoint file, along with any of its children.
2017-10-14 21:18:43 +00:00
|
|
|
/**
|
2018-01-26 02:50:58 +00:00
|
|
|
* CustomResource is a resource whose create, read, update, and delete (CRUD) operations are managed
|
|
|
|
* by performing external operations on some physical entity. The engine understands how to diff
|
|
|
|
* and perform partial updates of them, and these CRUD operations are implemented in a dynamically
|
|
|
|
* loaded plugin for the defining package.
|
Implement components
This change implements core support for "components" in the Pulumi
Fabric. This work is described further in pulumi/pulumi#340, where
we are still discussing some of the finer points.
In a nutshell, resources no longer imply external providers. It's
entirely possible to have a resource that logically represents
something but without having a physical manifestation that needs to
be tracked and managed by our typical CRUD operations.
For example, the aws/serverless/Function helper is one such type.
It aggregates Lambda-related resources and exposes a nice interface.
All of the Pulumi Cloud Framework resources are also examples.
To indicate that a resource does participate in the usual CRUD resource
provider, it simply derives from ExternalResource instead of Resource.
All resources now have the ability to adopt children. This is purely
a metadata/tagging thing, and will help us roll up displays, provide
attribution to the developer, and even hide aspects of the resource
graph as appropriate (e.g., when they are implementation details).
Our use of this capability is ultra limited right now; in fact, the
only place we display children is in the CLI output. For instance:
+ aws:serverless:Function: (create)
[urn=urn:pulumi:demo::serverless::aws:serverless:Function::mylambda]
=> urn:pulumi:demo::serverless::aws:iam/role:Role::mylambda-iamrole
=> urn:pulumi:demo::serverless::aws:iam/rolePolicyAttachment:RolePolicyAttachment::mylambda-iampolicy-0
=> urn:pulumi:demo::serverless::aws:lambda/function:Function::mylambda
The bit indicating whether a resource is external or not is tracked
in the resulting checkpoint file, along with any of its children.
2017-10-14 21:18:43 +00:00
|
|
|
*/
|
2017-10-15 10:52:04 +00:00
|
|
|
export abstract class CustomResource extends Resource {
|
Don't use `instanceof` for RTTI
This change moves us away from using JavaScript RTTI, by way of
`instanceof`, for built-in Pulumi types. If we use `instanceof`,
then the same logical type loaded from separate copies of the
SDK package -- as will happen in SxS scenarios -- are considered
different. This isn't actually what we want. The solution is
simple: implement our own quasi-RTTI solution, using __pulumi*
properties and manual as* and is* functions. Note that we could
have skipped the as* and is* functions, but I found that they led
to slightly easier to read code.
There is one strange thing in here, which I spoke to
@CyrusNajmabadi about: SerializedOutput<T>, because it implements
Output<T> as an _interface_, did not previously masquerade as an
actual Output<T>. In other words, `instanceof` would have returned
false, and indeed a few important properties (like promise) are
missing. This change preserves that behavior, although I'll admit
that this is slightly odd. I suspect we'll want to revisit this as
part of https://github.com/pulumi/pulumi/issues/1074.
Fixes https://github.com/pulumi/pulumi/issues/1203.
2018-04-16 21:03:37 +00:00
|
|
|
/**
|
|
|
|
* A private field to help with RTTI that works in SxS scenarios.
|
2020-01-26 17:06:35 +00:00
|
|
|
* @internal
|
Don't use `instanceof` for RTTI
This change moves us away from using JavaScript RTTI, by way of
`instanceof`, for built-in Pulumi types. If we use `instanceof`,
then the same logical type loaded from separate copies of the
SDK package -- as will happen in SxS scenarios -- are considered
different. This isn't actually what we want. The solution is
simple: implement our own quasi-RTTI solution, using __pulumi*
properties and manual as* and is* functions. Note that we could
have skipped the as* and is* functions, but I found that they led
to slightly easier to read code.
There is one strange thing in here, which I spoke to
@CyrusNajmabadi about: SerializedOutput<T>, because it implements
Output<T> as an _interface_, did not previously masquerade as an
actual Output<T>. In other words, `instanceof` would have returned
false, and indeed a few important properties (like promise) are
missing. This change preserves that behavior, although I'll admit
that this is slightly odd. I suspect we'll want to revisit this as
part of https://github.com/pulumi/pulumi/issues/1074.
Fixes https://github.com/pulumi/pulumi/issues/1203.
2018-04-16 21:03:37 +00:00
|
|
|
*/
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2019-04-24 02:24:06 +00:00
|
|
|
public readonly __pulumiCustomResource: boolean;
|
Don't use `instanceof` for RTTI
This change moves us away from using JavaScript RTTI, by way of
`instanceof`, for built-in Pulumi types. If we use `instanceof`,
then the same logical type loaded from separate copies of the
SDK package -- as will happen in SxS scenarios -- are considered
different. This isn't actually what we want. The solution is
simple: implement our own quasi-RTTI solution, using __pulumi*
properties and manual as* and is* functions. Note that we could
have skipped the as* and is* functions, but I found that they led
to slightly easier to read code.
There is one strange thing in here, which I spoke to
@CyrusNajmabadi about: SerializedOutput<T>, because it implements
Output<T> as an _interface_, did not previously masquerade as an
actual Output<T>. In other words, `instanceof` would have returned
false, and indeed a few important properties (like promise) are
missing. This change preserves that behavior, although I'll admit
that this is slightly odd. I suspect we'll want to revisit this as
part of https://github.com/pulumi/pulumi/issues/1074.
Fixes https://github.com/pulumi/pulumi/issues/1203.
2018-04-16 21:03:37 +00:00
|
|
|
|
Implement components
This change implements core support for "components" in the Pulumi
Fabric. This work is described further in pulumi/pulumi#340, where
we are still discussing some of the finer points.
In a nutshell, resources no longer imply external providers. It's
entirely possible to have a resource that logically represents
something but without having a physical manifestation that needs to
be tracked and managed by our typical CRUD operations.
For example, the aws/serverless/Function helper is one such type.
It aggregates Lambda-related resources and exposes a nice interface.
All of the Pulumi Cloud Framework resources are also examples.
To indicate that a resource does participate in the usual CRUD resource
provider, it simply derives from ExternalResource instead of Resource.
All resources now have the ability to adopt children. This is purely
a metadata/tagging thing, and will help us roll up displays, provide
attribution to the developer, and even hide aspects of the resource
graph as appropriate (e.g., when they are implementation details).
Our use of this capability is ultra limited right now; in fact, the
only place we display children is in the CLI output. For instance:
+ aws:serverless:Function: (create)
[urn=urn:pulumi:demo::serverless::aws:serverless:Function::mylambda]
=> urn:pulumi:demo::serverless::aws:iam/role:Role::mylambda-iamrole
=> urn:pulumi:demo::serverless::aws:iam/rolePolicyAttachment:RolePolicyAttachment::mylambda-iampolicy-0
=> urn:pulumi:demo::serverless::aws:lambda/function:Function::mylambda
The bit indicating whether a resource is external or not is tracked
in the resulting checkpoint file, along with any of its children.
2017-10-14 21:18:43 +00:00
|
|
|
/**
|
2018-01-26 02:50:58 +00:00
|
|
|
* id is the provider-assigned unique ID for this managed resource. It is set during
|
|
|
|
* deployments and may be missing (undefined) during planning phases.
|
Implement components
This change implements core support for "components" in the Pulumi
Fabric. This work is described further in pulumi/pulumi#340, where
we are still discussing some of the finer points.
In a nutshell, resources no longer imply external providers. It's
entirely possible to have a resource that logically represents
something but without having a physical manifestation that needs to
be tracked and managed by our typical CRUD operations.
For example, the aws/serverless/Function helper is one such type.
It aggregates Lambda-related resources and exposes a nice interface.
All of the Pulumi Cloud Framework resources are also examples.
To indicate that a resource does participate in the usual CRUD resource
provider, it simply derives from ExternalResource instead of Resource.
All resources now have the ability to adopt children. This is purely
a metadata/tagging thing, and will help us roll up displays, provide
attribution to the developer, and even hide aspects of the resource
graph as appropriate (e.g., when they are implementation details).
Our use of this capability is ultra limited right now; in fact, the
only place we display children is in the CLI output. For instance:
+ aws:serverless:Function: (create)
[urn=urn:pulumi:demo::serverless::aws:serverless:Function::mylambda]
=> urn:pulumi:demo::serverless::aws:iam/role:Role::mylambda-iamrole
=> urn:pulumi:demo::serverless::aws:iam/rolePolicyAttachment:RolePolicyAttachment::mylambda-iampolicy-0
=> urn:pulumi:demo::serverless::aws:lambda/function:Function::mylambda
The bit indicating whether a resource is external or not is tracked
in the resulting checkpoint file, along with any of its children.
2017-10-14 21:18:43 +00:00
|
|
|
*/
|
2019-09-11 23:21:35 +00:00
|
|
|
public readonly id!: Output<ID>;
|
Implement components
This change implements core support for "components" in the Pulumi
Fabric. This work is described further in pulumi/pulumi#340, where
we are still discussing some of the finer points.
In a nutshell, resources no longer imply external providers. It's
entirely possible to have a resource that logically represents
something but without having a physical manifestation that needs to
be tracked and managed by our typical CRUD operations.
For example, the aws/serverless/Function helper is one such type.
It aggregates Lambda-related resources and exposes a nice interface.
All of the Pulumi Cloud Framework resources are also examples.
To indicate that a resource does participate in the usual CRUD resource
provider, it simply derives from ExternalResource instead of Resource.
All resources now have the ability to adopt children. This is purely
a metadata/tagging thing, and will help us roll up displays, provide
attribution to the developer, and even hide aspects of the resource
graph as appropriate (e.g., when they are implementation details).
Our use of this capability is ultra limited right now; in fact, the
only place we display children is in the CLI output. For instance:
+ aws:serverless:Function: (create)
[urn=urn:pulumi:demo::serverless::aws:serverless:Function::mylambda]
=> urn:pulumi:demo::serverless::aws:iam/role:Role::mylambda-iamrole
=> urn:pulumi:demo::serverless::aws:iam/rolePolicyAttachment:RolePolicyAttachment::mylambda-iampolicy-0
=> urn:pulumi:demo::serverless::aws:lambda/function:Function::mylambda
The bit indicating whether a resource is external or not is tracked
in the resulting checkpoint file, along with any of its children.
2017-10-14 21:18:43 +00:00
|
|
|
|
Don't use `instanceof` for RTTI
This change moves us away from using JavaScript RTTI, by way of
`instanceof`, for built-in Pulumi types. If we use `instanceof`,
then the same logical type loaded from separate copies of the
SDK package -- as will happen in SxS scenarios -- are considered
different. This isn't actually what we want. The solution is
simple: implement our own quasi-RTTI solution, using __pulumi*
properties and manual as* and is* functions. Note that we could
have skipped the as* and is* functions, but I found that they led
to slightly easier to read code.
There is one strange thing in here, which I spoke to
@CyrusNajmabadi about: SerializedOutput<T>, because it implements
Output<T> as an _interface_, did not previously masquerade as an
actual Output<T>. In other words, `instanceof` would have returned
false, and indeed a few important properties (like promise) are
missing. This change preserves that behavior, although I'll admit
that this is slightly odd. I suspect we'll want to revisit this as
part of https://github.com/pulumi/pulumi/issues/1074.
Fixes https://github.com/pulumi/pulumi/issues/1203.
2018-04-16 21:03:37 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-04-16 22:03:23 +00:00
|
|
|
public static isInstance(obj: any): obj is CustomResource {
|
2019-02-28 22:56:35 +00:00
|
|
|
return utils.isInstance<CustomResource>(obj, "__pulumiCustomResource");
|
Don't use `instanceof` for RTTI
This change moves us away from using JavaScript RTTI, by way of
`instanceof`, for built-in Pulumi types. If we use `instanceof`,
then the same logical type loaded from separate copies of the
SDK package -- as will happen in SxS scenarios -- are considered
different. This isn't actually what we want. The solution is
simple: implement our own quasi-RTTI solution, using __pulumi*
properties and manual as* and is* functions. Note that we could
have skipped the as* and is* functions, but I found that they led
to slightly easier to read code.
There is one strange thing in here, which I spoke to
@CyrusNajmabadi about: SerializedOutput<T>, because it implements
Output<T> as an _interface_, did not previously masquerade as an
actual Output<T>. In other words, `instanceof` would have returned
false, and indeed a few important properties (like promise) are
missing. This change preserves that behavior, although I'll admit
that this is slightly odd. I suspect we'll want to revisit this as
part of https://github.com/pulumi/pulumi/issues/1074.
Fixes https://github.com/pulumi/pulumi/issues/1203.
2018-04-16 21:03:37 +00:00
|
|
|
}
|
|
|
|
|
Implement components
This change implements core support for "components" in the Pulumi
Fabric. This work is described further in pulumi/pulumi#340, where
we are still discussing some of the finer points.
In a nutshell, resources no longer imply external providers. It's
entirely possible to have a resource that logically represents
something but without having a physical manifestation that needs to
be tracked and managed by our typical CRUD operations.
For example, the aws/serverless/Function helper is one such type.
It aggregates Lambda-related resources and exposes a nice interface.
All of the Pulumi Cloud Framework resources are also examples.
To indicate that a resource does participate in the usual CRUD resource
provider, it simply derives from ExternalResource instead of Resource.
All resources now have the ability to adopt children. This is purely
a metadata/tagging thing, and will help us roll up displays, provide
attribution to the developer, and even hide aspects of the resource
graph as appropriate (e.g., when they are implementation details).
Our use of this capability is ultra limited right now; in fact, the
only place we display children is in the CLI output. For instance:
+ aws:serverless:Function: (create)
[urn=urn:pulumi:demo::serverless::aws:serverless:Function::mylambda]
=> urn:pulumi:demo::serverless::aws:iam/role:Role::mylambda-iamrole
=> urn:pulumi:demo::serverless::aws:iam/rolePolicyAttachment:RolePolicyAttachment::mylambda-iampolicy-0
=> urn:pulumi:demo::serverless::aws:lambda/function:Function::mylambda
The bit indicating whether a resource is external or not is tracked
in the resulting checkpoint file, along with any of its children.
2017-10-14 21:18:43 +00:00
|
|
|
/**
|
2018-01-26 02:50:58 +00:00
|
|
|
* Creates and registers a new managed resource. t 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.
|
|
|
|
* dependsOn is an optional list of other resources that this resource depends on, controlling
|
|
|
|
* the order in which we perform resource operations. Creating an instance does not necessarily
|
|
|
|
* perform a create on the physical entity which it represents, and instead, this is dependent
|
|
|
|
* upon the diffing of the new goal state compared to the current known resource state.
|
Implement components
This change implements core support for "components" in the Pulumi
Fabric. This work is described further in pulumi/pulumi#340, where
we are still discussing some of the finer points.
In a nutshell, resources no longer imply external providers. It's
entirely possible to have a resource that logically represents
something but without having a physical manifestation that needs to
be tracked and managed by our typical CRUD operations.
For example, the aws/serverless/Function helper is one such type.
It aggregates Lambda-related resources and exposes a nice interface.
All of the Pulumi Cloud Framework resources are also examples.
To indicate that a resource does participate in the usual CRUD resource
provider, it simply derives from ExternalResource instead of Resource.
All resources now have the ability to adopt children. This is purely
a metadata/tagging thing, and will help us roll up displays, provide
attribution to the developer, and even hide aspects of the resource
graph as appropriate (e.g., when they are implementation details).
Our use of this capability is ultra limited right now; in fact, the
only place we display children is in the CLI output. For instance:
+ aws:serverless:Function: (create)
[urn=urn:pulumi:demo::serverless::aws:serverless:Function::mylambda]
=> urn:pulumi:demo::serverless::aws:iam/role:Role::mylambda-iamrole
=> urn:pulumi:demo::serverless::aws:iam/rolePolicyAttachment:RolePolicyAttachment::mylambda-iampolicy-0
=> urn:pulumi:demo::serverless::aws:lambda/function:Function::mylambda
The bit indicating whether a resource is external or not is tracked
in the resulting checkpoint file, along with any of its children.
2017-10-14 21:18:43 +00:00
|
|
|
*
|
|
|
|
* @param t The type of the resource.
|
2018-02-07 23:01:55 +00:00
|
|
|
* @param name The _unique_ name of the resource.
|
Implement components
This change implements core support for "components" in the Pulumi
Fabric. This work is described further in pulumi/pulumi#340, where
we are still discussing some of the finer points.
In a nutshell, resources no longer imply external providers. It's
entirely possible to have a resource that logically represents
something but without having a physical manifestation that needs to
be tracked and managed by our typical CRUD operations.
For example, the aws/serverless/Function helper is one such type.
It aggregates Lambda-related resources and exposes a nice interface.
All of the Pulumi Cloud Framework resources are also examples.
To indicate that a resource does participate in the usual CRUD resource
provider, it simply derives from ExternalResource instead of Resource.
All resources now have the ability to adopt children. This is purely
a metadata/tagging thing, and will help us roll up displays, provide
attribution to the developer, and even hide aspects of the resource
graph as appropriate (e.g., when they are implementation details).
Our use of this capability is ultra limited right now; in fact, the
only place we display children is in the CLI output. For instance:
+ aws:serverless:Function: (create)
[urn=urn:pulumi:demo::serverless::aws:serverless:Function::mylambda]
=> urn:pulumi:demo::serverless::aws:iam/role:Role::mylambda-iamrole
=> urn:pulumi:demo::serverless::aws:iam/rolePolicyAttachment:RolePolicyAttachment::mylambda-iampolicy-0
=> urn:pulumi:demo::serverless::aws:lambda/function:Function::mylambda
The bit indicating whether a resource is external or not is tracked
in the resulting checkpoint file, along with any of its children.
2017-10-14 21:18:43 +00:00
|
|
|
* @param props The arguments to use to populate the new resource.
|
Implement resource protection (#751)
This change implements resource protection, as per pulumi/pulumi#689.
The overall idea is that a resource can be marked as "protect: true",
which will prevent deletion of that resource for any reason whatsoever
(straight deletion, replacement, etc). This is expressed in the
program. To "unprotect" a resource, one must perform an update setting
"protect: false", and then afterwards, they can delete the resource.
For example:
let res = new MyResource("precious", { .. }, { protect: true });
Afterwards, the resource will display in the CLI with a lock icon, and
any attempts to remove it will fail in the usual ways (in planning or,
worst case, during an actual update).
This was done by adding a new ResourceOptions bag parameter to the
base Resource types. This is unfortunately a breaking change, but now
is the right time to take this one. We had been adding new settings
one by one -- like parent and dependsOn -- and this new approach will
set us up to add any number of additional settings down the road,
without needing to worry about breaking anything ever again.
This is related to protected stacks, as described in
pulumi/pulumi-service#399. Most likely this will serve as a foundational
building block that enables the coarser grained policy management.
2017-12-20 22:31:07 +00:00
|
|
|
* @param opts A bag of options that control this resource's behavior.
|
Initial support for remote component construction. (#5280)
These changes add initial support for the construction of remote
components. For now, this support is limited to the NodeJS SDK;
follow-up changes will implement support for the other SDKs.
Remote components are component resources that are constructed and
managed by plugins rather than by Pulumi programs. In this sense, they
are a bit like cloud resources, and are supported by the same
distribution and plugin loading mechanisms and described by the same
schema system.
The construction of a remote component is initiated by a
`RegisterResourceRequest` with the new `remote` field set to `true`.
When the resource monitor receives such a request, it loads the plugin
that implements the component resource and calls the `Construct`
method added to the resource provider interface as part of these
changes. This method accepts the information necessary to construct the
component and its children: the component's name, type, resource
options, inputs, and input dependencies. It is responsible for
dispatching to the appropriate component factory to create the
component, then returning its URN, resolved output properties, and
output property dependencies. The dependency information is necessary to
support features such as delete-before-replace, which rely on precise
dependency information for custom resources.
These changes also add initial support for more conveniently
implementing resource providers in NodeJS. The interface used to
implement such a provider is similar to the dynamic provider interface
(and may be unified with that interface in the future).
An example of a NodeJS program constructing a remote component resource
also implemented in NodeJS can be found in
`tests/construct_component/nodejs`.
This is the core of #2430.
2020-09-08 02:33:55 +00:00
|
|
|
* @param dependency True if this is a synthetic resource used internally for dependency tracking.
|
Implement components
This change implements core support for "components" in the Pulumi
Fabric. This work is described further in pulumi/pulumi#340, where
we are still discussing some of the finer points.
In a nutshell, resources no longer imply external providers. It's
entirely possible to have a resource that logically represents
something but without having a physical manifestation that needs to
be tracked and managed by our typical CRUD operations.
For example, the aws/serverless/Function helper is one such type.
It aggregates Lambda-related resources and exposes a nice interface.
All of the Pulumi Cloud Framework resources are also examples.
To indicate that a resource does participate in the usual CRUD resource
provider, it simply derives from ExternalResource instead of Resource.
All resources now have the ability to adopt children. This is purely
a metadata/tagging thing, and will help us roll up displays, provide
attribution to the developer, and even hide aspects of the resource
graph as appropriate (e.g., when they are implementation details).
Our use of this capability is ultra limited right now; in fact, the
only place we display children is in the CLI output. For instance:
+ aws:serverless:Function: (create)
[urn=urn:pulumi:demo::serverless::aws:serverless:Function::mylambda]
=> urn:pulumi:demo::serverless::aws:iam/role:Role::mylambda-iamrole
=> urn:pulumi:demo::serverless::aws:iam/rolePolicyAttachment:RolePolicyAttachment::mylambda-iampolicy-0
=> urn:pulumi:demo::serverless::aws:lambda/function:Function::mylambda
The bit indicating whether a resource is external or not is tracked
in the resulting checkpoint file, along with any of its children.
2017-10-14 21:18:43 +00:00
|
|
|
*/
|
Initial support for remote component construction. (#5280)
These changes add initial support for the construction of remote
components. For now, this support is limited to the NodeJS SDK;
follow-up changes will implement support for the other SDKs.
Remote components are component resources that are constructed and
managed by plugins rather than by Pulumi programs. In this sense, they
are a bit like cloud resources, and are supported by the same
distribution and plugin loading mechanisms and described by the same
schema system.
The construction of a remote component is initiated by a
`RegisterResourceRequest` with the new `remote` field set to `true`.
When the resource monitor receives such a request, it loads the plugin
that implements the component resource and calls the `Construct`
method added to the resource provider interface as part of these
changes. This method accepts the information necessary to construct the
component and its children: the component's name, type, resource
options, inputs, and input dependencies. It is responsible for
dispatching to the appropriate component factory to create the
component, then returning its URN, resolved output properties, and
output property dependencies. The dependency information is necessary to
support features such as delete-before-replace, which rely on precise
dependency information for custom resources.
These changes also add initial support for more conveniently
implementing resource providers in NodeJS. The interface used to
implement such a provider is similar to the dynamic provider interface
(and may be unified with that interface in the future).
An example of a NodeJS program constructing a remote component resource
also implemented in NodeJS can be found in
`tests/construct_component/nodejs`.
This is the core of #2430.
2020-09-08 02:33:55 +00:00
|
|
|
constructor(t: string, name: string, props?: Inputs, opts: CustomResourceOptions = {}, dependency = false) {
|
2019-03-14 00:02:17 +00:00
|
|
|
if ((<ComponentResourceOptions>opts).providers) {
|
2023-04-28 22:27:10 +00:00
|
|
|
throw new ResourceError(
|
|
|
|
"Do not supply 'providers' option to a CustomResource. Did you mean 'provider' instead?",
|
|
|
|
opts.parent,
|
|
|
|
);
|
2019-03-14 00:02:17 +00:00
|
|
|
}
|
|
|
|
|
Initial support for remote component construction. (#5280)
These changes add initial support for the construction of remote
components. For now, this support is limited to the NodeJS SDK;
follow-up changes will implement support for the other SDKs.
Remote components are component resources that are constructed and
managed by plugins rather than by Pulumi programs. In this sense, they
are a bit like cloud resources, and are supported by the same
distribution and plugin loading mechanisms and described by the same
schema system.
The construction of a remote component is initiated by a
`RegisterResourceRequest` with the new `remote` field set to `true`.
When the resource monitor receives such a request, it loads the plugin
that implements the component resource and calls the `Construct`
method added to the resource provider interface as part of these
changes. This method accepts the information necessary to construct the
component and its children: the component's name, type, resource
options, inputs, and input dependencies. It is responsible for
dispatching to the appropriate component factory to create the
component, then returning its URN, resolved output properties, and
output property dependencies. The dependency information is necessary to
support features such as delete-before-replace, which rely on precise
dependency information for custom resources.
These changes also add initial support for more conveniently
implementing resource providers in NodeJS. The interface used to
implement such a provider is similar to the dynamic provider interface
(and may be unified with that interface in the future).
An example of a NodeJS program constructing a remote component resource
also implemented in NodeJS can be found in
`tests/construct_component/nodejs`.
This is the core of #2430.
2020-09-08 02:33:55 +00:00
|
|
|
super(t, name, true, props, opts, false, dependency);
|
2019-03-14 00:02:17 +00:00
|
|
|
this.__pulumiCustomResource = true;
|
Implement initial Lumi-as-a-library
This is the initial step towards redefining Lumi as a library that runs
atop vanilla Node.js/V8, rather than as its own runtime.
This change is woefully incomplete but this includes some of the more
stable pieces of my current work-in-progress.
The new structure is that within the sdk/ directory we will have a client
library per language. This client library contains the object model for
Lumi (resources, properties, assets, config, etc), in addition to the
"language runtime host" components required to interoperate with the
Lumi resource monitor. This resource monitor is effectively what we call
"Lumi" today, in that it's the thing orchestrating plans and deployments.
Inside the sdk/ directory, you will find nodejs/, the Node.js client
library, alongside proto/, the definitions for RPC interop between the
different pieces of the system. This includes existing RPC definitions
for resource providers, etc., in addition to the new ones for hosting
different language runtimes from within Lumi.
These new interfaces are surprisingly simple. There is effectively a
bidirectional RPC channel between the Lumi resource monitor, represented
by the lumirpc.ResourceMonitor interface, and each language runtime,
represented by the lumirpc.LanguageRuntime interface.
The overall orchestration goes as follows:
1) Lumi decides it needs to run a program written in language X, so
it dynamically loads the language runtime plugin for language X.
2) Lumi passes that runtime a loopback address to its ResourceMonitor
service, while language X will publish a connection back to its
LanguageRuntime service, which Lumi will talk to.
3) Lumi then invokes LanguageRuntime.Run, passing information like
the desired working directory, program name, arguments, and optional
configuration variables to make available to the program.
4) The language X runtime receives this, unpacks it and sets up the
necessary context, and then invokes the program. The program then
calls into Lumi object model abstractions that internally communicate
back to Lumi using the ResourceMonitor interface.
5) The key here is ResourceMonitor.NewResource, which Lumi uses to
serialize state about newly allocated resources. Lumi receives these
and registers them as part of the plan, doing the usual diffing, etc.,
to decide how to proceed. This interface is perhaps one of the
most subtle parts of the new design, as it necessitates the use of
promises internally to allow parallel evaluation of the resource plan,
letting dataflow determine the available concurrency.
6) The program exits, and Lumi continues on its merry way. If the program
fails, the RunResponse will include information about the failure.
Due to (5), all properties on resources are now instances of a new
Property<T> type. A Property<T> is just a thin wrapper over a T, but it
encodes the special properties of Lumi resource properties. Namely, it
is possible to create one out of a T, other Property<T>, Promise<T>, or
to freshly allocate one. In all cases, the Property<T> does not "settle"
until its final state is known. This cannot occur before the deployment
actually completes, and so in general it's not safe to depend on concrete
resolutions of values (unlike ordinary Promise<T>s which are usually
expected to resolve). As a result, all derived computations are meant to
use the `then` function (as in `someValue.then(v => v+x)`).
Although this change includes tests that may be run in isolation to test
the various RPC interactions, we are nowhere near finished. The remaining
work primarily boils down to three things:
1) Wiring all of this up to the Lumi code.
2) Fixing the handful of known loose ends required to make this work,
primarily around the serialization of properties (waiting on
unresolved ones, serializing assets properly, etc).
3) Implementing lambda closure serialization as a native extension.
This ongoing work is part of pulumi/pulumi-fabric#311.
2017-08-26 19:07:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-11 08:11:53 +00:00
|
|
|
(<any>CustomResource).doNotCapture = true;
|
|
|
|
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
/**
|
|
|
|
* ProviderResource is a resource that implements CRUD operations for other custom resources. These resources are
|
|
|
|
* managed similarly to other resources, including the usual diffing and update semantics.
|
|
|
|
*/
|
|
|
|
export abstract class ProviderResource extends CustomResource {
|
2019-06-18 17:54:14 +00:00
|
|
|
/** @internal */
|
|
|
|
private readonly pkg: string;
|
|
|
|
|
2019-10-15 05:08:06 +00:00
|
|
|
/** @internal */
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2019-10-15 05:08:06 +00:00
|
|
|
public __registrationId?: string;
|
|
|
|
|
|
|
|
public static async register(provider: ProviderResource | undefined): Promise<string | undefined> {
|
|
|
|
if (provider === undefined) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!provider.__registrationId) {
|
2019-10-16 22:19:43 +00:00
|
|
|
const providerURN = await provider.urn.promise();
|
2023-04-28 22:27:10 +00:00
|
|
|
const providerID = (await provider.id.promise()) || unknownValue;
|
2019-10-16 22:19:43 +00:00
|
|
|
provider.__registrationId = `${providerURN}::${providerID}`;
|
2019-10-15 05:08:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return provider.__registrationId;
|
|
|
|
}
|
|
|
|
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
/**
|
|
|
|
* Creates and registers a new provider resource for a particular package.
|
|
|
|
*
|
|
|
|
* @param pkg The package associated with this provider.
|
|
|
|
* @param name The _unique_ name of the provider.
|
|
|
|
* @param props The configuration to use for this provider.
|
|
|
|
* @param opts A bag of options that control this provider's behavior.
|
Initial support for remote component construction. (#5280)
These changes add initial support for the construction of remote
components. For now, this support is limited to the NodeJS SDK;
follow-up changes will implement support for the other SDKs.
Remote components are component resources that are constructed and
managed by plugins rather than by Pulumi programs. In this sense, they
are a bit like cloud resources, and are supported by the same
distribution and plugin loading mechanisms and described by the same
schema system.
The construction of a remote component is initiated by a
`RegisterResourceRequest` with the new `remote` field set to `true`.
When the resource monitor receives such a request, it loads the plugin
that implements the component resource and calls the `Construct`
method added to the resource provider interface as part of these
changes. This method accepts the information necessary to construct the
component and its children: the component's name, type, resource
options, inputs, and input dependencies. It is responsible for
dispatching to the appropriate component factory to create the
component, then returning its URN, resolved output properties, and
output property dependencies. The dependency information is necessary to
support features such as delete-before-replace, which rely on precise
dependency information for custom resources.
These changes also add initial support for more conveniently
implementing resource providers in NodeJS. The interface used to
implement such a provider is similar to the dynamic provider interface
(and may be unified with that interface in the future).
An example of a NodeJS program constructing a remote component resource
also implemented in NodeJS can be found in
`tests/construct_component/nodejs`.
This is the core of #2430.
2020-09-08 02:33:55 +00:00
|
|
|
* @param dependency True if this is a synthetic resource used internally for dependency tracking.
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
*/
|
Initial support for remote component construction. (#5280)
These changes add initial support for the construction of remote
components. For now, this support is limited to the NodeJS SDK;
follow-up changes will implement support for the other SDKs.
Remote components are component resources that are constructed and
managed by plugins rather than by Pulumi programs. In this sense, they
are a bit like cloud resources, and are supported by the same
distribution and plugin loading mechanisms and described by the same
schema system.
The construction of a remote component is initiated by a
`RegisterResourceRequest` with the new `remote` field set to `true`.
When the resource monitor receives such a request, it loads the plugin
that implements the component resource and calls the `Construct`
method added to the resource provider interface as part of these
changes. This method accepts the information necessary to construct the
component and its children: the component's name, type, resource
options, inputs, and input dependencies. It is responsible for
dispatching to the appropriate component factory to create the
component, then returning its URN, resolved output properties, and
output property dependencies. The dependency information is necessary to
support features such as delete-before-replace, which rely on precise
dependency information for custom resources.
These changes also add initial support for more conveniently
implementing resource providers in NodeJS. The interface used to
implement such a provider is similar to the dynamic provider interface
(and may be unified with that interface in the future).
An example of a NodeJS program constructing a remote component resource
also implemented in NodeJS can be found in
`tests/construct_component/nodejs`.
This is the core of #2430.
2020-09-08 02:33:55 +00:00
|
|
|
constructor(pkg: string, name: string, props?: Inputs, opts: ResourceOptions = {}, dependency: boolean = false) {
|
|
|
|
super(`pulumi:providers:${pkg}`, name, props, opts, dependency);
|
2019-06-18 17:54:14 +00:00
|
|
|
this.pkg = pkg;
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
}
|
2019-06-06 23:20:12 +00:00
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
public getPackage() {
|
|
|
|
return this.pkg;
|
|
|
|
}
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 10:52:04 +00:00
|
|
|
/**
|
2018-01-26 02:50:58 +00:00
|
|
|
* 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
|
|
|
|
* operations for provisioning.
|
2017-10-15 10:52:04 +00:00
|
|
|
*/
|
2019-12-17 23:34:30 +00:00
|
|
|
export class ComponentResource<TData = any> extends Resource {
|
2019-03-06 01:06:57 +00:00
|
|
|
/**
|
|
|
|
* A private field to help with RTTI that works in SxS scenarios.
|
2020-01-26 17:06:35 +00:00
|
|
|
* @internal
|
2019-03-06 01:06:57 +00:00
|
|
|
*/
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2019-12-17 23:34:30 +00:00
|
|
|
public readonly __pulumiComponentResource = true;
|
|
|
|
|
|
|
|
/** @internal */
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2019-12-17 23:34:30 +00:00
|
|
|
public readonly __data: Promise<TData>;
|
|
|
|
|
2019-12-18 09:46:29 +00:00
|
|
|
/** @internal */
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2019-12-17 23:34:30 +00:00
|
|
|
private __registered = false;
|
2019-03-06 01:06:57 +00:00
|
|
|
|
2021-07-16 23:11:34 +00:00
|
|
|
/** @internal */
|
2021-08-10 18:31:59 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
|
2021-07-16 23:11:34 +00:00
|
|
|
public readonly __remote: boolean;
|
|
|
|
|
2019-03-06 01:06:57 +00:00
|
|
|
/**
|
|
|
|
* 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");
|
|
|
|
}
|
|
|
|
|
2017-10-15 10:52:04 +00:00
|
|
|
/**
|
2018-12-15 23:32:19 +00:00
|
|
|
* 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.
|
|
|
|
* [opts.parent] is the optional parent for this component, and [opts.dependsOn] is an optional
|
|
|
|
* list of other resources that this resource depends on, controlling the order in which we
|
|
|
|
* perform resource operations.
|
2017-10-15 10:52:04 +00:00
|
|
|
*
|
2024-03-25 14:33:15 +00:00
|
|
|
* @param type The type of the resource.
|
2018-02-07 23:01:55 +00:00
|
|
|
* @param name The _unique_ name of the resource.
|
2019-12-17 23:34:30 +00:00
|
|
|
* @param args Information passed to [initialize] method.
|
Implement resource protection (#751)
This change implements resource protection, as per pulumi/pulumi#689.
The overall idea is that a resource can be marked as "protect: true",
which will prevent deletion of that resource for any reason whatsoever
(straight deletion, replacement, etc). This is expressed in the
program. To "unprotect" a resource, one must perform an update setting
"protect: false", and then afterwards, they can delete the resource.
For example:
let res = new MyResource("precious", { .. }, { protect: true });
Afterwards, the resource will display in the CLI with a lock icon, and
any attempts to remove it will fail in the usual ways (in planning or,
worst case, during an actual update).
This was done by adding a new ResourceOptions bag parameter to the
base Resource types. This is unfortunately a breaking change, but now
is the right time to take this one. We had been adding new settings
one by one -- like parent and dependsOn -- and this new approach will
set us up to add any number of additional settings down the road,
without needing to worry about breaking anything ever again.
This is related to protected stacks, as described in
pulumi/pulumi-service#399. Most likely this will serve as a foundational
building block that enables the coarser grained policy management.
2017-12-20 22:31:07 +00:00
|
|
|
* @param opts A bag of options that control this resource's behavior.
|
Initial support for remote component construction. (#5280)
These changes add initial support for the construction of remote
components. For now, this support is limited to the NodeJS SDK;
follow-up changes will implement support for the other SDKs.
Remote components are component resources that are constructed and
managed by plugins rather than by Pulumi programs. In this sense, they
are a bit like cloud resources, and are supported by the same
distribution and plugin loading mechanisms and described by the same
schema system.
The construction of a remote component is initiated by a
`RegisterResourceRequest` with the new `remote` field set to `true`.
When the resource monitor receives such a request, it loads the plugin
that implements the component resource and calls the `Construct`
method added to the resource provider interface as part of these
changes. This method accepts the information necessary to construct the
component and its children: the component's name, type, resource
options, inputs, and input dependencies. It is responsible for
dispatching to the appropriate component factory to create the
component, then returning its URN, resolved output properties, and
output property dependencies. The dependency information is necessary to
support features such as delete-before-replace, which rely on precise
dependency information for custom resources.
These changes also add initial support for more conveniently
implementing resource providers in NodeJS. The interface used to
implement such a provider is similar to the dynamic provider interface
(and may be unified with that interface in the future).
An example of a NodeJS program constructing a remote component resource
also implemented in NodeJS can be found in
`tests/construct_component/nodejs`.
This is the core of #2430.
2020-09-08 02:33:55 +00:00
|
|
|
* @param remote True if this is a remote component resource.
|
2017-10-15 10:52:04 +00:00
|
|
|
*/
|
2023-04-28 22:27:10 +00:00
|
|
|
constructor(
|
|
|
|
type: string,
|
|
|
|
name: string,
|
|
|
|
args: Inputs = {},
|
|
|
|
opts: ComponentResourceOptions = {},
|
|
|
|
remote: boolean = false,
|
|
|
|
) {
|
2018-12-15 23:32:19 +00:00
|
|
|
// Explicitly ignore the props passed in. We allow them for back compat reasons. However,
|
|
|
|
// we explicitly do not want to pass them along to the engine. The ComponentResource acts
|
|
|
|
// only as a container for other resources. Another way to think about this is that a normal
|
|
|
|
// 'custom resource' corresponds to real piece of cloud infrastructure. So, when it changes
|
|
|
|
// in some way, the cloud resource needs to be updated (and vice versa). That is not true
|
|
|
|
// for a component resource. The component is just used for organizational purposes and does
|
|
|
|
// 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.
|
2021-06-30 14:48:56 +00:00
|
|
|
super(type, name, /*custom:*/ false, /*props:*/ remote || opts?.urn ? args : {}, opts, remote);
|
2021-07-16 23:11:34 +00:00
|
|
|
this.__remote = remote;
|
2021-06-30 14:48:56 +00:00
|
|
|
this.__registered = remote || !!opts?.urn;
|
|
|
|
this.__data = remote || opts?.urn ? Promise.resolve(<TData>{}) : this.initializeAndRegisterOutputs(args);
|
2019-12-17 23:34:30 +00:00
|
|
|
}
|
|
|
|
|
2019-12-18 09:46:29 +00:00
|
|
|
/** @internal */
|
2019-12-17 23:34:30 +00:00
|
|
|
private async initializeAndRegisterOutputs(args: Inputs) {
|
|
|
|
const data = await this.initialize(args);
|
|
|
|
this.registerOutputs();
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Can be overridden by a subclass to asynchronously initialize data for this Component
|
|
|
|
* automatically when constructed. The data will be available immediately for subclass
|
|
|
|
* constructors to use. To access the data use `.getData`.
|
|
|
|
*/
|
|
|
|
protected async initialize(args: Inputs): Promise<TData> {
|
|
|
|
return <TData>undefined!;
|
2017-11-20 18:08:59 +00:00
|
|
|
}
|
|
|
|
|
2019-12-17 23:34:30 +00:00
|
|
|
/**
|
|
|
|
* Retrieves the data produces by [initialize]. The data is immediately available in a
|
|
|
|
* derived class's constructor after the `super(...)` call to `ComponentResource`.
|
|
|
|
*/
|
|
|
|
protected getData(): Promise<TData> {
|
|
|
|
return this.__data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* registerOutputs registers synthetic outputs that a component has initialized, usually by
|
|
|
|
* allocating other child sub-resources and propagating their resulting property values.
|
|
|
|
*
|
|
|
|
* ComponentResources can call this at the end of their constructor to indicate that they are
|
|
|
|
* done creating child resources. This is not strictly necessary as this will automatically be
|
|
|
|
* called after the `initialize` method completes.
|
|
|
|
*/
|
2018-12-15 23:32:19 +00:00
|
|
|
protected registerOutputs(outputs?: Inputs | Promise<Inputs> | Output<Inputs>): void {
|
2019-12-17 23:34:30 +00:00
|
|
|
if (this.__registered) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.__registered = true;
|
2019-01-27 01:59:11 +00:00
|
|
|
registerResourceOutputs(this, outputs || {});
|
2017-10-15 10:52:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-11 08:11:53 +00:00
|
|
|
(<any>ComponentResource).doNotCapture = true;
|
|
|
|
(<any>ComponentResource.prototype).registerOutputs.doNotCapture = true;
|
2019-12-18 09:46:29 +00:00
|
|
|
(<any>ComponentResource.prototype).initialize.doNotCapture = true;
|
|
|
|
(<any>ComponentResource.prototype).initializeAndRegisterOutputs.doNotCapture = true;
|
2018-03-11 08:11:53 +00:00
|
|
|
|
2019-04-24 02:24:06 +00:00
|
|
|
/** @internal */
|
2018-09-26 04:29:27 +00:00
|
|
|
export const testingOptions = {
|
|
|
|
isDryRun: false,
|
|
|
|
};
|
2019-07-29 19:01:10 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* [mergeOptions] takes two ResourceOptions values and produces a new ResourceOptions with the
|
|
|
|
* respective properties of `opts2` merged over the same properties in `opts1`. The original
|
|
|
|
* options objects will be unchanged.
|
|
|
|
*
|
|
|
|
* Conceptually property merging follows these basic rules:
|
|
|
|
* 1. if the property is a collection, the final value will be a collection containing the values
|
|
|
|
* from each options object.
|
|
|
|
* 2. Simple scaler values from `opts2` (i.e. strings, numbers, bools) will replace the values of
|
|
|
|
* `opts1`.
|
|
|
|
* 3. `opts2` can have properties explicitly provided with `null` or `undefined` as the value. If
|
|
|
|
* explicitly provided, then that will be the final value in the result.
|
|
|
|
* 4. For the purposes of merging `dependsOn`, `provider` and `providers` are always treated as
|
|
|
|
* collections, even if only a single value was provided.
|
|
|
|
*/
|
2023-04-28 22:27:10 +00:00
|
|
|
export function mergeOptions(
|
|
|
|
opts1: CustomResourceOptions | undefined,
|
|
|
|
opts2: CustomResourceOptions | undefined,
|
|
|
|
): CustomResourceOptions;
|
|
|
|
export function mergeOptions(
|
|
|
|
opts1: ComponentResourceOptions | undefined,
|
|
|
|
opts2: ComponentResourceOptions | undefined,
|
|
|
|
): ComponentResourceOptions;
|
2019-07-29 19:01:10 +00:00
|
|
|
export function mergeOptions(opts1: ResourceOptions | undefined, opts2: ResourceOptions | undefined): ResourceOptions;
|
|
|
|
export function mergeOptions(opts1: ResourceOptions | undefined, opts2: ResourceOptions | undefined): ResourceOptions {
|
|
|
|
const dest = <any>{ ...opts1 };
|
|
|
|
const source = <any>{ ...opts2 };
|
|
|
|
|
|
|
|
// Ensure provider/providers are all expanded into the `ProviderResource[]` form.
|
|
|
|
// This makes merging simple.
|
|
|
|
expandProviders(dest);
|
|
|
|
expandProviders(source);
|
|
|
|
|
|
|
|
// iterate specifically over the supplied properties in [source]. Note: there may not be an
|
|
|
|
// corresponding value in [dest].
|
|
|
|
for (const key of Object.keys(source)) {
|
|
|
|
const destVal = dest[key];
|
|
|
|
const sourceVal = source[key];
|
|
|
|
|
|
|
|
// For 'dependsOn' we might have singleton resources in both options bags. We
|
|
|
|
// want to make sure we combine them into a collection.
|
|
|
|
if (key === "dependsOn") {
|
|
|
|
dest[key] = merge(destVal, sourceVal, /*alwaysCreateArray:*/ true);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
dest[key] = merge(destVal, sourceVal, /*alwaysCreateArray:*/ false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now, if we are left with a .providers that is just a single key/value pair, then
|
|
|
|
// collapse that down into .provider form.
|
|
|
|
normalizeProviders(dest);
|
|
|
|
|
|
|
|
return dest;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isPromiseOrOutput(val: any): boolean {
|
|
|
|
return val instanceof Promise || Output.isInstance(val);
|
|
|
|
}
|
|
|
|
|
2021-04-19 22:41:53 +00:00
|
|
|
/** @internal */
|
|
|
|
export function expandProviders(options: ComponentResourceOptions) {
|
2019-07-29 19:01:10 +00:00
|
|
|
// Convert 'providers' map to array form.
|
|
|
|
if (options.providers && !Array.isArray(options.providers)) {
|
2022-01-25 19:11:41 +00:00
|
|
|
for (const k in options.providers) {
|
|
|
|
if (Object.prototype.hasOwnProperty.call(options.providers, k)) {
|
|
|
|
const v = options.providers[k];
|
|
|
|
if (k !== v.getPackage()) {
|
|
|
|
const message = `provider resource map where key ${k} doesn't match provider ${v.getPackage()}`;
|
|
|
|
log.warn(message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-08 19:11:46 +00:00
|
|
|
options.providers = utils.values(options.providers);
|
2019-07-29 19:01:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function normalizeProviders(opts: ComponentResourceOptions) {
|
2022-01-25 19:11:41 +00:00
|
|
|
// If we have 0 providers, delete providers. Otherwise, convert providers into a map.
|
2019-07-29 19:01:10 +00:00
|
|
|
const providers = <ProviderResource[]>opts.providers;
|
|
|
|
if (providers) {
|
|
|
|
if (providers.length === 0) {
|
2023-04-29 01:59:40 +00:00
|
|
|
opts.providers = undefined;
|
2023-04-28 22:27:10 +00:00
|
|
|
} else {
|
2019-07-29 19:01:10 +00:00
|
|
|
opts.providers = {};
|
|
|
|
for (const res of providers) {
|
|
|
|
opts.providers[res.getPackage()] = res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @internal for testing purposes. */
|
|
|
|
export function merge(dest: any, source: any, alwaysCreateArray: boolean): any {
|
|
|
|
// unwind any top level promise/outputs.
|
|
|
|
if (isPromiseOrOutput(dest)) {
|
2023-04-28 22:27:10 +00:00
|
|
|
return output(dest).apply((d) => merge(d, source, alwaysCreateArray));
|
2019-07-29 19:01:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (isPromiseOrOutput(source)) {
|
2023-04-28 22:27:10 +00:00
|
|
|
return output(source).apply((s) => merge(dest, s, alwaysCreateArray));
|
2019-07-29 19:01:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If either are an array, make a new array and merge the values into it.
|
|
|
|
// Otherwise, just overwrite the destination with the source value.
|
|
|
|
if (alwaysCreateArray || Array.isArray(dest) || Array.isArray(source)) {
|
|
|
|
const result: any[] = [];
|
|
|
|
addToArray(result, dest);
|
|
|
|
addToArray(result, source);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return source;
|
|
|
|
}
|
|
|
|
|
|
|
|
function addToArray(resultArray: any[], value: any) {
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
resultArray.push(...value);
|
2023-04-28 22:27:10 +00:00
|
|
|
} else if (value !== undefined && value !== null) {
|
2019-07-29 19:01:10 +00:00
|
|
|
resultArray.push(value);
|
|
|
|
}
|
|
|
|
}
|
Initial support for remote component construction. (#5280)
These changes add initial support for the construction of remote
components. For now, this support is limited to the NodeJS SDK;
follow-up changes will implement support for the other SDKs.
Remote components are component resources that are constructed and
managed by plugins rather than by Pulumi programs. In this sense, they
are a bit like cloud resources, and are supported by the same
distribution and plugin loading mechanisms and described by the same
schema system.
The construction of a remote component is initiated by a
`RegisterResourceRequest` with the new `remote` field set to `true`.
When the resource monitor receives such a request, it loads the plugin
that implements the component resource and calls the `Construct`
method added to the resource provider interface as part of these
changes. This method accepts the information necessary to construct the
component and its children: the component's name, type, resource
options, inputs, and input dependencies. It is responsible for
dispatching to the appropriate component factory to create the
component, then returning its URN, resolved output properties, and
output property dependencies. The dependency information is necessary to
support features such as delete-before-replace, which rely on precise
dependency information for custom resources.
These changes also add initial support for more conveniently
implementing resource providers in NodeJS. The interface used to
implement such a provider is similar to the dynamic provider interface
(and may be unified with that interface in the future).
An example of a NodeJS program constructing a remote component resource
also implemented in NodeJS can be found in
`tests/construct_component/nodejs`.
This is the core of #2430.
2020-09-08 02:33:55 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A DependencyResource is a resource that is used to indicate that an Output has a dependency on a particular
|
|
|
|
* resource. These resources are only created when dealing with remote component resources.
|
|
|
|
*/
|
|
|
|
export class DependencyResource extends CustomResource {
|
|
|
|
constructor(urn: URN) {
|
|
|
|
super("", "", {}, {}, true);
|
|
|
|
|
2023-04-28 22:27:10 +00:00
|
|
|
(<any>this).urn = new Output(
|
|
|
|
<any>this,
|
|
|
|
Promise.resolve(urn),
|
|
|
|
Promise.resolve(true),
|
|
|
|
Promise.resolve(false),
|
|
|
|
Promise.resolve([]),
|
|
|
|
);
|
Initial support for remote component construction. (#5280)
These changes add initial support for the construction of remote
components. For now, this support is limited to the NodeJS SDK;
follow-up changes will implement support for the other SDKs.
Remote components are component resources that are constructed and
managed by plugins rather than by Pulumi programs. In this sense, they
are a bit like cloud resources, and are supported by the same
distribution and plugin loading mechanisms and described by the same
schema system.
The construction of a remote component is initiated by a
`RegisterResourceRequest` with the new `remote` field set to `true`.
When the resource monitor receives such a request, it loads the plugin
that implements the component resource and calls the `Construct`
method added to the resource provider interface as part of these
changes. This method accepts the information necessary to construct the
component and its children: the component's name, type, resource
options, inputs, and input dependencies. It is responsible for
dispatching to the appropriate component factory to create the
component, then returning its URN, resolved output properties, and
output property dependencies. The dependency information is necessary to
support features such as delete-before-replace, which rely on precise
dependency information for custom resources.
These changes also add initial support for more conveniently
implementing resource providers in NodeJS. The interface used to
implement such a provider is similar to the dynamic provider interface
(and may be unified with that interface in the future).
An example of a NodeJS program constructing a remote component resource
also implemented in NodeJS can be found in
`tests/construct_component/nodejs`.
This is the core of #2430.
2020-09-08 02:33:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A DependencyProviderResource is a resource that is used by the provider SDK as a stand-in for a provider that
|
|
|
|
* is only used for its reference. Its only valid properties are its URN and ID.
|
|
|
|
*/
|
|
|
|
export class DependencyProviderResource extends ProviderResource {
|
|
|
|
constructor(ref: string) {
|
2021-07-23 21:10:06 +00:00
|
|
|
const [urn, id] = parseResourceReference(ref);
|
2021-07-27 13:50:24 +00:00
|
|
|
const urnParts = urn.split("::");
|
|
|
|
const qualifiedType = urnParts[2];
|
|
|
|
const type = qualifiedType.split("$").pop()!;
|
|
|
|
// type will be "pulumi:providers:<package>" and we want the last part.
|
|
|
|
const typeParts = type.split(":");
|
|
|
|
const pkg = typeParts.length > 2 ? typeParts[2] : "";
|
|
|
|
|
|
|
|
super(pkg, "", {}, {}, true);
|
|
|
|
|
2023-04-28 22:27:10 +00:00
|
|
|
(<any>this).urn = new Output(
|
|
|
|
<any>this,
|
|
|
|
Promise.resolve(urn),
|
|
|
|
Promise.resolve(true),
|
|
|
|
Promise.resolve(false),
|
|
|
|
Promise.resolve([]),
|
|
|
|
);
|
|
|
|
(<any>this).id = new Output(
|
|
|
|
<any>this,
|
|
|
|
Promise.resolve(id),
|
|
|
|
Promise.resolve(true),
|
|
|
|
Promise.resolve(false),
|
|
|
|
Promise.resolve([]),
|
|
|
|
);
|
Initial support for remote component construction. (#5280)
These changes add initial support for the construction of remote
components. For now, this support is limited to the NodeJS SDK;
follow-up changes will implement support for the other SDKs.
Remote components are component resources that are constructed and
managed by plugins rather than by Pulumi programs. In this sense, they
are a bit like cloud resources, and are supported by the same
distribution and plugin loading mechanisms and described by the same
schema system.
The construction of a remote component is initiated by a
`RegisterResourceRequest` with the new `remote` field set to `true`.
When the resource monitor receives such a request, it loads the plugin
that implements the component resource and calls the `Construct`
method added to the resource provider interface as part of these
changes. This method accepts the information necessary to construct the
component and its children: the component's name, type, resource
options, inputs, and input dependencies. It is responsible for
dispatching to the appropriate component factory to create the
component, then returning its URN, resolved output properties, and
output property dependencies. The dependency information is necessary to
support features such as delete-before-replace, which rely on precise
dependency information for custom resources.
These changes also add initial support for more conveniently
implementing resource providers in NodeJS. The interface used to
implement such a provider is similar to the dynamic provider interface
(and may be unified with that interface in the future).
An example of a NodeJS program constructing a remote component resource
also implemented in NodeJS can be found in
`tests/construct_component/nodejs`.
This is the core of #2430.
2020-09-08 02:33:55 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-23 21:10:06 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* parseResourceReference parses the URN and ID out of the provider reference.
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
export function parseResourceReference(ref: string): [string, string] {
|
|
|
|
const lastSep = ref.lastIndexOf("::");
|
|
|
|
if (lastSep === -1) {
|
|
|
|
throw new Error(`expected '::' in provider reference ${ref}`);
|
|
|
|
}
|
|
|
|
const urn = ref.slice(0, lastSep);
|
2023-04-28 22:27:10 +00:00
|
|
|
const id = ref.slice(lastSep + 2);
|
2021-07-23 21:10:06 +00:00
|
|
|
return [urn, id];
|
|
|
|
}
|