2018-05-22 21:43:36 +02: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 21:07:54 +02:00
|
|
|
|
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 23:26:28 +02:00
|
|
|
import { util } from "protobufjs";
|
2019-10-15 07:08:06 +02:00
|
|
|
import { ResourceError } from "./errors";
|
|
|
|
import { Input, Inputs, interpolate, Output, output } from "./output";
|
|
|
|
import { getStackResource, unknownValue } from "./runtime";
|
2018-09-12 04:38:45 +02:00
|
|
|
import { readResource, registerResource, registerResourceOutputs } from "./runtime/resource";
|
2019-06-01 08:01:01 +02:00
|
|
|
import { getProject, getStack } from "./runtime/settings";
|
2019-02-28 23:56:35 +01: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 21:07:54 +02:00
|
|
|
|
|
|
|
export type ID = string; // a provider-assigned ID.
|
|
|
|
export type URN = string; // an automatically generated logical URN, used to stably identify resources.
|
|
|
|
|
2019-06-01 08:01:01 +02:00
|
|
|
/**
|
|
|
|
* createUrn computes a URN from the combination of a resource name, resource type, optional parent,
|
|
|
|
* optional project and optional stack.
|
|
|
|
*/
|
|
|
|
export function createUrn(name: Input<string>, type: Input<string>, parent?: Resource | Input<URN>, project?: string, stack?: string): Output<string> {
|
|
|
|
let parentPrefix: Output<string>;
|
|
|
|
if (parent) {
|
|
|
|
let parentUrn: Output<string>;
|
|
|
|
if (Resource.isInstance(parent)) {
|
|
|
|
parentUrn = parent.urn;
|
|
|
|
} else {
|
|
|
|
parentUrn = output(parent);
|
|
|
|
}
|
|
|
|
parentPrefix = parentUrn.apply(parentUrnString => parentUrnString.substring(0, parentUrnString.lastIndexOf("::")) + "$");
|
|
|
|
} else {
|
|
|
|
parentPrefix = output(`urn:pulumi:${stack || getStack()}::${project || getProject()}::`);
|
|
|
|
}
|
|
|
|
return interpolate`${parentPrefix}${type}::${name}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
function inheritedChildAlias(childName: string, parentName: string, parentAlias: Input<string>, childType: string): Output<string> {
|
|
|
|
// 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)) {
|
|
|
|
aliasName = output(parentAlias).apply(parentAliasUrn => {
|
|
|
|
const parentAliasName = parentAliasUrn.substring(parentAliasUrn.lastIndexOf("::") + 2);
|
|
|
|
return parentAliasName + childName.substring(parentName.length);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return createUrn(aliasName, childType, parentAlias);
|
|
|
|
}
|
|
|
|
|
2017-09-22 03:15:29 +02: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 21:07:54 +02:00
|
|
|
export abstract class Resource {
|
2018-04-25 02:23:18 +02:00
|
|
|
/**
|
2019-04-24 04:24:06 +02:00
|
|
|
* @internal
|
2018-04-25 02:23:18 +02:00
|
|
|
* A private field to help with RTTI that works in SxS scenarios.
|
|
|
|
*/
|
2019-06-01 08:01:01 +02:00
|
|
|
// tslint:disable-next-line:variable-name
|
|
|
|
public readonly __pulumiResource: boolean = true;
|
2018-04-25 02:23:18 +02:00
|
|
|
|
2019-03-06 02:06:57 +01:00
|
|
|
/**
|
2019-04-24 04:24:06 +02:00
|
|
|
* @internal
|
2019-03-06 02:06:57 +01:00
|
|
|
* The optional parent of this resource.
|
|
|
|
*/
|
|
|
|
// tslint:disable-next-line:variable-name
|
2019-04-24 04:24:06 +02:00
|
|
|
public readonly __parentResource: Resource | undefined;
|
2019-03-06 02:06:57 +01:00
|
|
|
|
|
|
|
/**
|
2019-04-24 04:24:06 +02:00
|
|
|
* @internal
|
2019-03-06 02:06:57 +01: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.
|
|
|
|
*/
|
|
|
|
// tslint:disable-next-line:variable-name
|
2019-04-24 04:24:06 +02:00
|
|
|
public __childResources: Set<Resource> | undefined;
|
2019-03-06 02:06:57 +01:00
|
|
|
|
2017-09-22 03:15:29 +02:00
|
|
|
/**
|
2018-01-26 03:50:58 +01:00
|
|
|
* urn is the stable logical URN used to distinctly address a resource, both before and after
|
|
|
|
* deployments.
|
2017-09-22 03:15:29 +02:00
|
|
|
*/
|
2019-09-12 01:21:35 +02: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 23:18:43 +02:00
|
|
|
|
2018-08-11 01:18:21 +02:00
|
|
|
/**
|
2019-04-24 04:24:06 +02:00
|
|
|
* @internal
|
2018-08-11 01:18:21 +02:00
|
|
|
* When set to true, protect ensures this resource cannot be deleted.
|
|
|
|
*/
|
2019-02-22 05:18:29 +01:00
|
|
|
// tslint:disable-next-line:variable-name
|
2019-04-24 04:24:06 +02:00
|
|
|
private readonly __protect: boolean;
|
2018-08-11 01:18:21 +02: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 20:27:37 +02:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
// tslint:disable-next-line:variable-name
|
|
|
|
__transformations?: ResourceTransformation[];
|
|
|
|
|
2019-06-01 08:01:01 +02:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
* A list of aliases applied to this resource.
|
2019-07-16 20:15:26 +02: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 20:27:37 +02:00
|
|
|
* this property, and marking optional forces consumers of the property to defensively handle
|
2019-07-16 20:15:26 +02:00
|
|
|
* cases where they are passed "old" resources.
|
2019-06-01 08:01:01 +02:00
|
|
|
*/
|
|
|
|
// tslint:disable-next-line:variable-name
|
2019-07-16 20:15:26 +02:00
|
|
|
readonly __aliases?: Input<URN>[];
|
2019-06-01 08:01:01 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
* The name assigned to the resource at construction.
|
2019-07-16 20:15:26 +02: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 20:27:37 +02:00
|
|
|
* this property, and marking optional forces consumers of the property to defensively handle
|
2019-07-16 20:15:26 +02:00
|
|
|
* cases where they are passed "old" resources.
|
2019-06-01 08:01:01 +02:00
|
|
|
*/
|
|
|
|
// tslint:disable-next-line:variable-name
|
2019-07-16 20:15:26 +02:00
|
|
|
private readonly __name?: string;
|
2019-06-01 08:01:01 +02:00
|
|
|
|
2018-08-11 01:18:21 +02:00
|
|
|
/**
|
2019-04-24 04:24:06 +02:00
|
|
|
* @internal
|
2018-08-11 01:18:21 +02:00
|
|
|
* The set of providers to use for child resources. Keyed by package name (e.g. "aws").
|
|
|
|
*/
|
2019-06-01 08:01:01 +02:00
|
|
|
// tslint:disable-next-line:variable-name
|
2019-04-24 04:24:06 +02:00
|
|
|
private readonly __providers: Record<string, ProviderResource>;
|
2018-08-11 01:18:21 +02:00
|
|
|
|
2018-04-25 02:23:18 +02:00
|
|
|
public static isInstance(obj: any): obj is Resource {
|
2019-02-28 23:56:35 +01:00
|
|
|
return utils.isInstance<Resource>(obj, "__pulumiResource");
|
2018-04-25 02:23:18 +02:00
|
|
|
}
|
|
|
|
|
2018-08-11 01:18:21 +02:00
|
|
|
// getProvider fetches the provider for the given module member, if any.
|
|
|
|
public getProvider(moduleMember: string): ProviderResource | undefined {
|
|
|
|
const memComponents = moduleMember.split(":");
|
|
|
|
if (memComponents.length !== 3) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
const pkg = memComponents[0];
|
|
|
|
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 23:18:43 +02:00
|
|
|
/**
|
2018-12-18 01:00:31 +01: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 03:50:58 +01: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 03:15:29 +02:00
|
|
|
*
|
|
|
|
* @param t The type of the resource.
|
2018-02-08 00:01:55 +01:00
|
|
|
* @param name The _unique_ name of the resource.
|
2017-10-15 12:52:04 +02:00
|
|
|
* @param custom True to indicate that this is a custom resource, managed by a plugin.
|
2017-09-22 03:15:29 +02: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 23:31:07 +01:00
|
|
|
* @param opts A bag of options that control this resource's behavior.
|
2017-09-22 03:15:29 +02:00
|
|
|
*/
|
2018-04-07 19:15:58 +02:00
|
|
|
constructor(t: string, name: string, custom: boolean, props: Inputs = {}, opts: ResourceOptions = {}) {
|
2019-03-13 20:21:33 +01: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 23:18:43 +02:00
|
|
|
if (!t) {
|
2018-09-25 01:57:20 +02: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 21:07:54 +02: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 23:18:43 +02:00
|
|
|
if (!name) {
|
2018-09-25 01:57:20 +02: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 21:07:54 +02:00
|
|
|
}
|
|
|
|
|
2019-10-15 02:35:00 +02:00
|
|
|
// Before anything else - if there are transformations registered, invoke them in order to transform the properties and
|
|
|
|
// options assigned to this resource.
|
|
|
|
const parent = opts.parent || getStackResource() || { __transformations: undefined };
|
|
|
|
this.__transformations = [ ...(opts.transformations || []), ...(parent.__transformations || []) ];
|
|
|
|
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 08:01:01 +02:00
|
|
|
this.__name = name;
|
|
|
|
|
|
|
|
// Make a shallow clone of opts to ensure we don't modify the value passed in.
|
|
|
|
opts = Object.assign({}, opts);
|
|
|
|
|
2019-06-07 01:20:12 +02:00
|
|
|
if (opts.provider && (<ComponentResourceOptions>opts).providers) {
|
|
|
|
throw new ResourceError("Do not supply both 'provider' and 'providers' options to a ComponentResource.", opts.parent);
|
|
|
|
}
|
|
|
|
|
2018-09-18 20:47:34 +02:00
|
|
|
// Check the parent type if one exists and fill in any default options.
|
2018-08-11 01:18:21 +02:00
|
|
|
this.__providers = {};
|
|
|
|
if (opts.parent) {
|
2019-03-06 02:06:57 +01:00
|
|
|
this.__parentResource = opts.parent;
|
|
|
|
this.__parentResource.__childResources = this.__parentResource.__childResources || new Set();
|
|
|
|
this.__parentResource.__childResources.add(this);
|
|
|
|
|
2018-08-11 01:18:21 +02:00
|
|
|
if (opts.protect === undefined) {
|
|
|
|
opts.protect = opts.parent.__protect;
|
|
|
|
}
|
|
|
|
|
2019-06-01 08:01:01 +02:00
|
|
|
// Make a copy of the aliases array, and add to it any implicit aliases inherited from its parent
|
|
|
|
opts.aliases = [...(opts.aliases || [])];
|
2019-07-16 20:15:26 +02:00
|
|
|
if (opts.parent.__name) {
|
|
|
|
for (const parentAlias of (opts.parent.__aliases || [])) {
|
|
|
|
opts.aliases.push(inheritedChildAlias(name, opts.parent.__name, parentAlias, t));
|
|
|
|
}
|
2019-06-01 08:01:01 +02:00
|
|
|
}
|
|
|
|
|
2018-08-11 01:18:21 +02:00
|
|
|
this.__providers = opts.parent.__providers;
|
2019-06-12 01:57:37 +02:00
|
|
|
}
|
2018-09-07 18:42:19 +02:00
|
|
|
|
2019-06-12 01:57:37 +02:00
|
|
|
if (custom) {
|
2019-10-18 02:12:45 +02:00
|
|
|
const provider = opts.provider;
|
2019-06-12 01:57:37 +02:00
|
|
|
if (provider === undefined) {
|
|
|
|
if (opts.parent) {
|
|
|
|
// If no provider was given, but we have a parent, then inherit the
|
|
|
|
// provider from our parent.
|
2019-10-18 02:12:45 +02:00
|
|
|
opts.provider = opts.parent.getProvider(t);
|
2019-06-12 01:57:37 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If a provider was specified, add it to the providers map under this type's package so that
|
|
|
|
// any children of this resource inherit its provider.
|
|
|
|
const typeComponents = t.split(":");
|
|
|
|
if (typeComponents.length === 3) {
|
|
|
|
const pkg = typeComponents[0];
|
|
|
|
this.__providers = { ...this.__providers, [pkg]: provider };
|
2018-08-11 01:18:21 +02:00
|
|
|
}
|
2018-09-07 18:42:19 +02:00
|
|
|
}
|
|
|
|
}
|
2019-06-12 01:57:37 +02:00
|
|
|
else {
|
2019-06-07 01:20:12 +02:00
|
|
|
// Note: we checked above that at most one of opts.provider or opts.providers is set.
|
|
|
|
|
|
|
|
// If opts.provider is set, treat that as if we were given a array of provider with that
|
|
|
|
// single value in it. Otherwise, take the array or map of providers, convert it to a
|
|
|
|
// map and combine with any providers we've already set from our parent.
|
|
|
|
const providers = opts.provider
|
|
|
|
? convertToProvidersMap([opts.provider])
|
|
|
|
: convertToProvidersMap((<ComponentResourceOptions>opts).providers);
|
|
|
|
this.__providers = { ...this.__providers, ...providers };
|
2018-04-25 02:23:18 +02:00
|
|
|
}
|
2019-06-07 01:20:12 +02:00
|
|
|
|
2018-08-11 01:18:21 +02:00
|
|
|
this.__protect = !!opts.protect;
|
2018-04-25 02:23:18 +02:00
|
|
|
|
2019-06-01 08:01:01 +02: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) {
|
|
|
|
this.__aliases.push(collapseAliasToUrn(alias, name, t, opts.parent));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-07 19:15:58 +02:00
|
|
|
if (opts.id) {
|
2018-04-05 18:48:09 +02:00
|
|
|
// If this resource already exists, read its state rather than registering it anew.
|
|
|
|
if (!custom) {
|
2018-09-25 01:57:20 +02:00
|
|
|
throw new ResourceError(
|
|
|
|
"Cannot read an existing resource unless it has a custom provider", opts.parent);
|
2018-04-05 18:48:09 +02:00
|
|
|
}
|
2018-04-07 19:15:58 +02:00
|
|
|
readResource(this, t, name, props, opts);
|
2018-04-05 18:48:09 +02: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.
|
|
|
|
registerResource(this, t, name, custom, props, opts);
|
|
|
|
}
|
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 23:18:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-07 01:20:12 +02: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 09:11:53 +01:00
|
|
|
(<any>Resource).doNotCapture = true;
|
|
|
|
|
2019-06-01 08:01:01 +02:00
|
|
|
/**
|
2019-06-21 00:53:33 +02: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 08:01:01 +02:00
|
|
|
*/
|
|
|
|
export interface Alias {
|
|
|
|
/**
|
2019-06-21 00:53:33 +02:00
|
|
|
* The previous name of the resource. If not provided, the current name of the resource is
|
|
|
|
* used.
|
2019-06-01 08:01:01 +02: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-21 00:53:33 +02:00
|
|
|
|
2019-06-01 08:01:01 +02:00
|
|
|
/**
|
2019-06-21 00:53:33 +02: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 08:01:01 +02: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>;
|
|
|
|
}
|
|
|
|
|
|
|
|
// collapseAliasToUrn turns an Alias into a URN given a set of default data
|
|
|
|
function collapseAliasToUrn(
|
2019-06-21 00:53:33 +02:00
|
|
|
alias: Input<Alias | string>,
|
|
|
|
defaultName: string,
|
|
|
|
defaultType: string,
|
|
|
|
defaultParent: Resource | undefined): Output<URN> {
|
2019-06-01 08:01:01 +02:00
|
|
|
|
|
|
|
return output(alias).apply(a => {
|
|
|
|
if (typeof a === "string") {
|
|
|
|
return output(a);
|
|
|
|
}
|
2019-06-21 00:53:33 +02: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 08:01:01 +02: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 23:31:07 +01:00
|
|
|
/**
|
|
|
|
* ResourceOptions is a bag of optional settings that control a resource's behavior.
|
|
|
|
*/
|
|
|
|
export interface ResourceOptions {
|
2019-07-29 21:01:10 +02: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-04-07 19:15:58 +02: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 23:31:07 +01:00
|
|
|
/**
|
|
|
|
* An optional parent resource to which this resource belongs.
|
|
|
|
*/
|
|
|
|
parent?: Resource;
|
|
|
|
/**
|
|
|
|
* An optional additional explicit dependencies on other resources.
|
|
|
|
*/
|
2018-11-19 17:22:55 +01: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 23:31:07 +01:00
|
|
|
/**
|
|
|
|
* When set to true, protect ensures this resource cannot be deleted.
|
|
|
|
*/
|
|
|
|
protect?: boolean;
|
2019-04-22 22:54:48 +02:00
|
|
|
/**
|
|
|
|
* Ignore changes to any of the specified properties.
|
|
|
|
*/
|
|
|
|
ignoreChanges?: string[];
|
2019-04-23 20:02:51 +02: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 08:01:01 +02:00
|
|
|
/**
|
2019-06-07 01:20:12 +02:00
|
|
|
* An optional list of aliases to treat this resource as matching.
|
2019-06-01 08:01:01 +02:00
|
|
|
*/
|
|
|
|
aliases?: Input<URN | Alias>[];
|
2019-06-07 01:20:12 +02: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 23:26:28 +02: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 20:27:37 +02: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[];
|
2019-07-29 21:01:10 +02: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 23:26:28 +02: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-11 01:18:21 +02: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 20:27:37 +02: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;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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-11 01:18:21 +02:00
|
|
|
/**
|
|
|
|
* CustomResourceOptions is a bag of optional settings that control a custom resource's behavior.
|
|
|
|
*/
|
|
|
|
export interface CustomResourceOptions extends ResourceOptions {
|
2019-07-29 21:01:10 +02: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 23:27:53 +01: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 19:15:17 +02: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 20:12:01 +02: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 21:01:10 +02: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 23:31:07 +01:00
|
|
|
}
|
|
|
|
|
2018-08-11 01:18:21 +02:00
|
|
|
/**
|
|
|
|
* ComponentResourceOptions is a bag of optional settings that control a component resource's behavior.
|
|
|
|
*/
|
|
|
|
export interface ComponentResourceOptions extends ResourceOptions {
|
2019-07-29 21:01:10 +02: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-11 01:18:21 +02:00
|
|
|
/**
|
2019-06-07 01:20:12 +02: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.
|
|
|
|
*
|
|
|
|
* In the case of a single provider, the options can be simplified to just pass along `provider: theProvider`
|
|
|
|
*
|
|
|
|
* Note: do not provide both [provider] and [providers];
|
2018-08-11 01:18:21 +02:00
|
|
|
*/
|
2019-06-07 01:20:12 +02:00
|
|
|
providers?: Record<string, ProviderResource> | ProviderResource[];
|
2019-07-29 21:01:10 +02: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-11 01:18:21 +02: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 23:18:43 +02:00
|
|
|
/**
|
2018-01-26 03:50:58 +01: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 23:18:43 +02:00
|
|
|
*/
|
2017-10-15 12:52:04 +02: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 23:03:37 +02:00
|
|
|
/**
|
2019-04-24 04:24:06 +02: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 23:03:37 +02:00
|
|
|
* A private field to help with RTTI that works in SxS scenarios.
|
|
|
|
*/
|
|
|
|
// tslint:disable-next-line:variable-name
|
2019-04-24 04:24:06 +02: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 23:03:37 +02:00
|
|
|
|
2019-05-01 01:07:01 +02:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
* Private field containing the type ID for this object. Useful for implementing `isInstance` on
|
|
|
|
* classes that inherit from `CustomResource`.
|
|
|
|
*/
|
|
|
|
// tslint:disable-next-line:variable-name
|
|
|
|
public readonly __pulumiType: string;
|
|
|
|
|
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 23:18:43 +02:00
|
|
|
/**
|
2018-01-26 03:50:58 +01: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 23:18:43 +02:00
|
|
|
*/
|
2019-09-12 01:21:35 +02: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 23:18:43 +02: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 23:03:37 +02: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-17 00:03:23 +02:00
|
|
|
public static isInstance(obj: any): obj is CustomResource {
|
2019-02-28 23:56:35 +01: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 23:03:37 +02: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 23:18:43 +02:00
|
|
|
/**
|
2018-01-26 03:50:58 +01: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 23:18:43 +02:00
|
|
|
*
|
|
|
|
* @param t The type of the resource.
|
2018-02-08 00:01:55 +01: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 23:18:43 +02: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 23:31:07 +01:00
|
|
|
* @param opts A bag of options that control this resource's behavior.
|
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 23:18:43 +02:00
|
|
|
*/
|
2019-03-14 01:02:17 +01:00
|
|
|
constructor(t: string, name: string, props?: Inputs, opts: CustomResourceOptions = {}) {
|
|
|
|
if ((<ComponentResourceOptions>opts).providers) {
|
|
|
|
throw new ResourceError("Do not supply 'providers' option to a CustomResource. Did you mean 'provider' instead?", opts.parent);
|
|
|
|
}
|
|
|
|
|
2018-04-07 19:15:58 +02:00
|
|
|
super(t, name, true, props, opts);
|
2019-03-14 01:02:17 +01:00
|
|
|
this.__pulumiCustomResource = true;
|
2019-05-01 01:07:01 +02:00
|
|
|
this.__pulumiType = t;
|
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 21:07:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-11 09:11:53 +01: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 02:50:29 +02: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 19:54:14 +02:00
|
|
|
/** @internal */
|
|
|
|
private readonly pkg: string;
|
|
|
|
|
2019-10-15 07:08:06 +02:00
|
|
|
/** @internal */
|
|
|
|
// tslint:disable-next-line: variable-name
|
|
|
|
public __registrationId?: string;
|
|
|
|
|
|
|
|
public static async register(provider: ProviderResource | undefined): Promise<string | undefined> {
|
|
|
|
if (provider === undefined) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!provider.__registrationId) {
|
2019-10-17 00:19:43 +02:00
|
|
|
const providerURN = await provider.urn.promise();
|
|
|
|
const providerID = await provider.id.promise() || unknownValue;
|
|
|
|
provider.__registrationId = `${providerURN}::${providerID}`;
|
2019-10-15 07:08:06 +02: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 02:50:29 +02: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.
|
|
|
|
*/
|
2019-06-18 19:54:14 +02:00
|
|
|
constructor(pkg: string, name: string, props?: Inputs, opts: ResourceOptions = {}) {
|
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 02:50:29 +02:00
|
|
|
super(`pulumi:providers:${pkg}`, name, props, opts);
|
2019-06-18 19:54:14 +02: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 02:50:29 +02:00
|
|
|
}
|
2019-06-07 01:20:12 +02: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 02:50:29 +02:00
|
|
|
}
|
|
|
|
|
2017-10-15 12:52:04 +02:00
|
|
|
/**
|
2018-01-26 03:50:58 +01: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 12:52:04 +02:00
|
|
|
*/
|
2017-11-18 00:22:41 +01:00
|
|
|
export class ComponentResource extends Resource {
|
2019-03-06 02:06:57 +01:00
|
|
|
/**
|
2019-04-24 04:24:06 +02:00
|
|
|
* @internal
|
2019-03-06 02:06:57 +01:00
|
|
|
* A private field to help with RTTI that works in SxS scenarios.
|
|
|
|
*/
|
|
|
|
// tslint:disable-next-line:variable-name
|
2019-04-24 04:24:06 +02:00
|
|
|
public readonly __pulumiComponentResource: boolean;
|
2019-03-06 02:06:57 +01: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 12:52:04 +02:00
|
|
|
/**
|
2018-12-16 00:32:19 +01: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 12:52:04 +02:00
|
|
|
*
|
|
|
|
* @param t The type of the resource.
|
2018-02-08 00:01:55 +01:00
|
|
|
* @param name The _unique_ name of the resource.
|
2018-12-16 00:32:19 +01:00
|
|
|
* @param unused [Deprecated]. Component resources do not communicate or store their properties
|
|
|
|
* with the Pulumi engine.
|
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 23:31:07 +01:00
|
|
|
* @param opts A bag of options that control this resource's behavior.
|
2017-10-15 12:52:04 +02:00
|
|
|
*/
|
2018-12-16 00:32:19 +01:00
|
|
|
constructor(type: string, name: string, unused?: Inputs, opts: ComponentResourceOptions = {}) {
|
|
|
|
// 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.
|
|
|
|
super(type, name, /*custom:*/ false, /*props:*/ {}, opts);
|
2019-03-06 02:06:57 +01:00
|
|
|
this.__pulumiComponentResource = true;
|
2017-11-20 19:08:59 +01:00
|
|
|
}
|
|
|
|
|
2018-12-16 00:32:19 +01:00
|
|
|
// registerOutputs registers synthetic outputs that a component has initialized, usually by
|
|
|
|
// allocating other child sub-resources and propagating their resulting property values.
|
|
|
|
// ComponentResources should always call this at the end of their constructor to indicate that
|
|
|
|
// they are done creating child resources. While not strictly necessary, this helps the
|
|
|
|
// experience by ensuring the UI transitions the ComponentResource to the 'complete' state as
|
|
|
|
// quickly as possible (instead of waiting until the entire application completes).
|
|
|
|
protected registerOutputs(outputs?: Inputs | Promise<Inputs> | Output<Inputs>): void {
|
2019-01-27 02:59:11 +01:00
|
|
|
registerResourceOutputs(this, outputs || {});
|
2017-10-15 12:52:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-11 09:11:53 +01:00
|
|
|
(<any>ComponentResource).doNotCapture = true;
|
|
|
|
(<any>ComponentResource.prototype).registerOutputs.doNotCapture = true;
|
|
|
|
|
2019-04-24 04:24:06 +02:00
|
|
|
/** @internal */
|
2018-09-26 06:29:27 +02:00
|
|
|
export const testingOptions = {
|
|
|
|
isDryRun: false,
|
|
|
|
};
|
2019-07-29 21:01:10 +02: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.
|
|
|
|
*/
|
|
|
|
export function mergeOptions(opts1: CustomResourceOptions | undefined, opts2: CustomResourceOptions | undefined): CustomResourceOptions;
|
|
|
|
export function mergeOptions(opts1: ComponentResourceOptions | undefined, opts2: ComponentResourceOptions | undefined): ComponentResourceOptions;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
function expandProviders(options: ComponentResourceOptions) {
|
|
|
|
// Move 'provider' up to 'providers' if we have it.
|
|
|
|
if (options.provider) {
|
|
|
|
options.providers = [options.provider];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert 'providers' map to array form.
|
|
|
|
if (options.providers && !Array.isArray(options.providers)) {
|
2019-08-08 21:11:46 +02:00
|
|
|
options.providers = utils.values(options.providers);
|
2019-07-29 21:01:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
delete options.provider;
|
|
|
|
}
|
|
|
|
|
|
|
|
function normalizeProviders(opts: ComponentResourceOptions) {
|
|
|
|
// If we have only 0-1 providers, then merge that back down to the .provider field.
|
|
|
|
const providers = <ProviderResource[]>opts.providers;
|
|
|
|
if (providers) {
|
|
|
|
if (providers.length === 0) {
|
|
|
|
delete opts.providers;
|
|
|
|
}
|
|
|
|
else if (providers.length === 1) {
|
|
|
|
opts.provider = providers[0];
|
|
|
|
delete opts.providers;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
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)) {
|
|
|
|
return output(dest).apply(d => merge(d, source, alwaysCreateArray));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isPromiseOrOutput(source)) {
|
|
|
|
return output(source).apply(s => merge(dest, s, alwaysCreateArray));
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
else if (value !== undefined && value !== null) {
|
|
|
|
resultArray.push(value);
|
|
|
|
}
|
|
|
|
}
|