pulumi/sdk/nodejs/resource.ts
Joe Duffy bc2cf55463
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 14:31:07 -08:00

142 lines
6.4 KiB
TypeScript

// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
import { registerResource, registerResourceOutputs } from "./runtime/resource";
import { getRootResource } from "./runtime/settings";
export type ID = string; // a provider-assigned ID.
export type URN = string; // an automatically generated logical URN, used to stably identify resources.
/**
* Resource represents a class whose CRUD operations are implemented by a provider plugin.
*/
export abstract class Resource {
/**
* urn is the stable logical URN used to distinctly address a resource, both before and after deployments.
*/
public readonly urn: Promise<URN>;
/**
* 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. dependsOn is an optional list of other
* resources that this resource depends on, controlling the order in which we perform resource operations.
*
* @param t The type of the resource.
* @param name The _unqiue_ name of the resource.
* @param custom True to indicate that this is a custom resource, managed by a plugin.
* @param props The arguments to use to populate the new resource.
* @param opts A bag of options that control this resource's behavior.
*/
constructor(t: string, name: string, custom: boolean, props?: ComputedValues, opts?: ResourceOptions) {
if (!t) {
throw new Error("Missing resource type argument");
}
if (!name) {
throw new Error("Missing resource name argument (for URN creation)");
}
// If there wasn't an explicit parent, and a root resource exists, parent to that.
opts = opts || {};
if (!opts.parent) {
opts.parent = getRootResource();
}
// Now 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);
}
}
/**
* ResourceOptions is a bag of optional settings that control a resource's behavior.
*/
export interface ResourceOptions {
/**
* An optional parent resource to which this resource belongs.
*/
parent?: Resource;
/**
* An optional additional explicit dependencies on other resources.
*/
dependsOn?: Resource[];
/**
* When set to true, protect ensures this resource cannot be deleted.
*/
protect?: boolean;
}
/**
* 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.
*/
export abstract class CustomResource extends Resource {
/**
* id is the provider-assigned unique ID for this managed resource. It is set during deployments and may be
* missing (undefined) during planning phases.
*/
public readonly id: Computed<ID>;
/**
* 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.
*
* @param t The type of the resource.
* @param name The _unqiue_ name of the resource.
* @param props The arguments to use to populate the new resource.
* @param opts A bag of options that control this resource's behavior.
*/
constructor(t: string, name: string, props?: ComputedValues, opts?: ResourceOptions) {
super(t, name, true, props, opts);
}
}
/**
* 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.
*/
export class ComponentResource extends Resource {
/**
* Creates and registers a new component 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. parent is the optional parent for this
* component, and dependsOn is an optional list of other resources that this resource depends on, controlling the
* order in which we perform resource operations.
*
* @param t The type of the resource.
* @param name The _unqiue_ name of the resource.
* @param props The arguments to use to populate the new resource.
* @param opts A bag of options that control this resource's behavior.
* @param protect True to ensure this resource cannot be deleted.
*/
constructor(t: string, name: string, props?: ComputedValues, opts?: ResourceOptions) {
super(t, name, false, props, opts);
}
// registerOutputs registers synthetic outputs that a component has initialized, usually by allocating
// other child sub-resources and propagating their resulting property values.
protected registerOutputs(outputs: ComputedValues | undefined): void {
if (outputs) {
registerResourceOutputs(this, outputs);
}
}
}
/**
* Computed is a property output for a resource. It is just a promise that also permits undefined values. The
* undefined values are used during planning, when the actual final value of a resource may not yet be known.
*/
export type Computed<T> = Promise<T | undefined>;
/**
* ComputedValue is a property input for a resource. It may be a promptly available T or a promise for one.
*/
export type ComputedValue<T> = T | undefined | Promise<T | undefined>;
/**
* ComputedValues is a map of property name to optional property input, one for each resource property value.
*/
export type ComputedValues = { [key: string]: ComputedValue<any> };