mirror of https://github.com/pulumi/pulumi.git
392 lines
13 KiB
TypeScript
392 lines
13 KiB
TypeScript
// Copyright 2016-2022, 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.
|
|
|
|
import { Inputs, Input, Output } from "../output";
|
|
import * as resource from "../resource";
|
|
import { getProject } from "../metadata";
|
|
import * as pulumiConfig from "../config";
|
|
import * as settings from "../runtime/settings";
|
|
import * as serializeClosure from "../runtime/closure/serializeClosure";
|
|
|
|
/**
|
|
* Config is a wrapper around {@link pulumi.Config} that can be used in
|
|
* dynamic providers. This class does not provide methods to retrieve secrets
|
|
* in the form of {@link pulumi.Output}. Since these methods are called at
|
|
* runtime, secrets will not be serialized as part of Pulumi's dynamic provider
|
|
* implementation, and it is safe to use the plain get and require methods
|
|
* instead. Do note however that you should not log or print the values.
|
|
*/
|
|
export class Config {
|
|
/**
|
|
* name is the configuration bag's logical name and uniquely identifies it. The default is the name of the current
|
|
* project.
|
|
*/
|
|
public readonly name: string;
|
|
private readonly config: pulumiConfig.Config;
|
|
|
|
constructor(name?: string) {
|
|
if (name === undefined) {
|
|
name = getProject();
|
|
}
|
|
this.name = name;
|
|
this.config = new pulumiConfig.Config(name);
|
|
}
|
|
|
|
/**
|
|
* get loads an optional configuration value by its key, or undefined if it doesn't exist.
|
|
*
|
|
* @param key The key to lookup.
|
|
* @param opts An options bag to constrain legal values.
|
|
*/
|
|
public get<K extends string = string>(key: string, opts?: pulumiConfig.StringConfigOptions<K>): K | undefined {
|
|
return this.config.get(key, opts);
|
|
}
|
|
|
|
/**
|
|
* getBoolean loads an optional configuration value, as a boolean, by its key, or undefined if it doesn't exist.
|
|
* If the configuration value isn't a legal boolean, this function will throw an error.
|
|
*
|
|
* @param key The key to lookup.
|
|
*/
|
|
public getBoolean(key: string): boolean | undefined {
|
|
return this.config.getBoolean(key);
|
|
}
|
|
|
|
/**
|
|
* getNumber loads an optional configuration value, as a number, by its key, or undefined if it doesn't exist.
|
|
* If the configuration value isn't a legal number, this function will throw an error.
|
|
*
|
|
* @param key The key to lookup.
|
|
* @param opts An options bag to constrain legal values.
|
|
*/
|
|
public getNumber(key: string, opts?: pulumiConfig.NumberConfigOptions): number | undefined {
|
|
return this.config.getNumber(key, opts);
|
|
}
|
|
|
|
/**
|
|
* getObject loads an optional configuration value, as an object, by its key, or undefined if it doesn't exist.
|
|
* This routine simply JSON parses and doesn't validate the shape of the contents.
|
|
*
|
|
* @param key The key to lookup.
|
|
*/
|
|
public getObject<T>(key: string): T | undefined {
|
|
return this.config.getObject(key);
|
|
}
|
|
|
|
/**
|
|
* require loads a configuration value by its given key. If it doesn't exist, an error is thrown.
|
|
*
|
|
* @param key The key to lookup.
|
|
* @param opts An options bag to constrain legal values.
|
|
*/
|
|
public require<K extends string = string>(key: string, opts?: pulumiConfig.StringConfigOptions<K>): K {
|
|
return this.config.require(key, opts);
|
|
}
|
|
|
|
/**
|
|
* requireBoolean loads a configuration value, as a boolean, by its given key. If it doesn't exist, or the
|
|
* configuration value is not a legal boolean, an error is thrown.
|
|
*
|
|
* @param key The key to lookup.
|
|
*/
|
|
public requireBoolean(key: string): boolean {
|
|
return this.config.requireBoolean(key);
|
|
}
|
|
|
|
/**
|
|
* requireNumber loads a configuration value, as a number, by its given key. If it doesn't exist, or the
|
|
* configuration value is not a legal number, an error is thrown.
|
|
*
|
|
* @param key The key to lookup.
|
|
* @param opts An options bag to constrain legal values.
|
|
*/
|
|
public requireNumber(key: string, opts?: pulumiConfig.NumberConfigOptions): number {
|
|
return this.config.requireNumber(key, opts);
|
|
}
|
|
|
|
/**
|
|
* requireObject loads a configuration value as a JSON string and deserializes the JSON into a JavaScript object. If
|
|
* it doesn't exist, or the configuration value is not a legal JSON string, an error is thrown.
|
|
*
|
|
* @param key The key to lookup.
|
|
*/
|
|
public requireObject<T>(key: string): T {
|
|
return this.config.requireObject(key);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@link CheckResult} represents the results of a call to {@link ResourceProvider.check}.
|
|
*/
|
|
export interface CheckResult<Inputs = any> {
|
|
/**
|
|
* The inputs to use, if any.
|
|
*/
|
|
readonly inputs?: Inputs;
|
|
|
|
/**
|
|
* Any validation failures that occurred.
|
|
*/
|
|
readonly failures?: CheckFailure[];
|
|
}
|
|
|
|
/**
|
|
* {@link CheckFailure} represents a single failure in the results of a call to
|
|
* {@link ResourceProvider.check}.
|
|
*/
|
|
export interface CheckFailure {
|
|
/**
|
|
* The property that failed validation.
|
|
*/
|
|
readonly property: string;
|
|
|
|
/**
|
|
* The reason that the property failed validation.
|
|
*/
|
|
readonly reason: string;
|
|
}
|
|
|
|
/**
|
|
* {@link DiffResult} represents the results of a call to
|
|
* {@link ResourceProvider.diff}.
|
|
*/
|
|
export interface DiffResult {
|
|
/**
|
|
* If true, this diff detected changes and suggests an update.
|
|
*/
|
|
readonly changes?: boolean;
|
|
|
|
/**
|
|
* If this update requires a replacement, the set of properties triggering it.
|
|
*/
|
|
readonly replaces?: string[];
|
|
|
|
/**
|
|
* An optional list of properties that will not ever change.
|
|
*/
|
|
readonly stables?: string[];
|
|
|
|
/**
|
|
* If true, and a replacement occurs, the resource will first be deleted
|
|
* before being recreated. This is to avoid potential side-by-side issues
|
|
* with the default create before delete behavior.
|
|
*/
|
|
readonly deleteBeforeReplace?: boolean;
|
|
}
|
|
|
|
/**
|
|
* {@link CreateResult} represents the results of a call to
|
|
* {@link ResourceProvider.create}.
|
|
*/
|
|
export interface CreateResult<Outputs = any> {
|
|
/**
|
|
* The ID of the created resource.
|
|
*/
|
|
readonly id: resource.ID;
|
|
|
|
/**
|
|
* Any properties that were computed during creation.
|
|
*/
|
|
readonly outs?: Outputs;
|
|
}
|
|
|
|
/**
|
|
* {@link ReadResult} represents the results of a call to
|
|
* {@link ResourceProvider.read}.
|
|
*/
|
|
export interface ReadResult<Outputs = any> {
|
|
/**
|
|
* The ID of the resource ready back (or blank if missing).
|
|
*/
|
|
readonly id?: resource.ID;
|
|
/**
|
|
* The current property state read from the live environment.
|
|
*/
|
|
readonly props?: Outputs;
|
|
}
|
|
|
|
/**
|
|
* {@link UpdateResult} represents the results of a call to
|
|
* {@link ResourceProvider.update}.
|
|
*/
|
|
export interface UpdateResult<Outputs = any> {
|
|
/**
|
|
* Any properties that were computed during updating.
|
|
*/
|
|
readonly outs?: Outputs;
|
|
}
|
|
|
|
/**
|
|
* {@link ResourceProvider} represents an object that provides CRUD operations
|
|
* for a particular type of resource.
|
|
*/
|
|
export interface ResourceProvider<Inputs = any, Outputs = any> {
|
|
/**
|
|
* Validates that the given property bag is valid for a resource of the given type.
|
|
*
|
|
* @param olds
|
|
* The old input properties to use for validation.
|
|
* @param news
|
|
* The new input properties to use for validation.
|
|
* @param config
|
|
* The config for the current stack.
|
|
*/
|
|
check?: (olds: Inputs, news: Inputs, config?: Config) => Promise<CheckResult<Inputs>>;
|
|
|
|
/**
|
|
* Checks what impacts a hypothetical update will have on the resource's
|
|
* properties.
|
|
*
|
|
* @param id
|
|
* The ID of the resource to diff.
|
|
* @param olds
|
|
* The old values of properties to diff.
|
|
* @param news
|
|
* The new values of properties to diff.
|
|
* @param config
|
|
* The config for the current stack.
|
|
*/
|
|
diff?: (id: resource.ID, olds: Outputs, news: Inputs, config?: Config) => Promise<DiffResult>;
|
|
|
|
/**
|
|
* Allocates a new instance of the provided resource and returns its unique
|
|
* ID afterwards. If this call fails, the resource must not have been
|
|
* created (i.e., it is "transactional").
|
|
*
|
|
* @param inputs
|
|
* The properties to set during creation.
|
|
* @param config
|
|
* The config for the current stack.
|
|
*/
|
|
create: (inputs: Inputs, config?: Config) => Promise<CreateResult<Outputs>>;
|
|
|
|
/**
|
|
* Reads the current live state associated with a resource. Enough state
|
|
* must be included in the inputs to uniquely identify the resource; this is
|
|
* typically just the resource ID, but it may also include some properties.
|
|
*
|
|
* @param id
|
|
* The ID of the resource to delete.
|
|
* @param props
|
|
* The current properties on the resource.
|
|
* @param config
|
|
* The config for the current stack.
|
|
*/
|
|
read?: (id: resource.ID, props?: Outputs, config?: Config) => Promise<ReadResult<Outputs>>;
|
|
|
|
/**
|
|
* Updates an existing resource with new values.
|
|
*
|
|
* @param id
|
|
* The ID of the resource to update.
|
|
* @param olds
|
|
* The old values of properties to update.
|
|
* @param news
|
|
* The new values of properties to update.
|
|
* @param config
|
|
* The config for the current stack.
|
|
*/
|
|
update?: (id: resource.ID, olds: Outputs, news: Inputs, config?: Config) => Promise<UpdateResult<Outputs>>;
|
|
|
|
/**
|
|
* Tears down an existing resource with the given ID. If it fails,
|
|
* the resource is assumed to still exist.
|
|
*
|
|
* @param id
|
|
* The ID of the resource to delete.
|
|
* @param props
|
|
* The current properties on the resource.
|
|
* @param config
|
|
* The config for the current stack.
|
|
*/
|
|
delete?: (id: resource.ID, props: Outputs, config?: Config) => Promise<void>;
|
|
}
|
|
|
|
const providerCache = new WeakMap<ResourceProvider, Input<string>>();
|
|
|
|
function serializeProvider(provider: ResourceProvider): Input<string> {
|
|
let result: Input<string>;
|
|
// caching is enabled by default as of 3.0
|
|
if (settings.cacheDynamicProviders()) {
|
|
const cachedProvider = providerCache.get(provider);
|
|
if (cachedProvider) {
|
|
result = cachedProvider;
|
|
} else {
|
|
result = serializeFunctionMaybeSecret(provider);
|
|
providerCache.set(provider, result);
|
|
}
|
|
} else {
|
|
result = serializeFunctionMaybeSecret(provider);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function serializeFunctionMaybeSecret(provider: ResourceProvider): Output<string> {
|
|
// Load runtime/closure on demand, as its dependencies are slow to load.
|
|
//
|
|
// See https://www.typescriptlang.org/docs/handbook/modules.html#optional-module-loading-and-other-advanced-loading-scenarios
|
|
const sc: typeof serializeClosure = require("../runtime/closure/serializeClosure");
|
|
|
|
const sfPromise = sc.serializeFunction(() => provider, { allowSecrets: true });
|
|
|
|
// Create an Output from the promise's text and containsSecrets properties. Uses the internal API since we don't provide a public interface for this.
|
|
return new Output(
|
|
[],
|
|
sfPromise.then((sf) => sf.text),
|
|
new Promise((resolve) => resolve(true)),
|
|
sfPromise.then((sf) => sf.containsSecrets),
|
|
new Promise((resolve) => resolve([])),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* {@link Resource} represents a Pulumi resource that incorporates an inline
|
|
* implementation of the Resource's CRUD operations.
|
|
*/
|
|
export abstract class Resource extends resource.CustomResource {
|
|
/**
|
|
* Creates a new dynamic resource.
|
|
*
|
|
* @param provider
|
|
* The implementation of the resource's CRUD operations.
|
|
* @param name
|
|
* The name of the resource.
|
|
* @param props
|
|
* The arguments to use to populate the new resource. Must not define the
|
|
* reserved property "__provider".
|
|
* @param opts
|
|
* A bag of options that control this resource's behavior.
|
|
* @param module
|
|
* The module of the resource.
|
|
* @param type
|
|
* The type of the resource.
|
|
*/
|
|
constructor(
|
|
provider: ResourceProvider,
|
|
name: string,
|
|
props: Inputs,
|
|
opts?: resource.CustomResourceOptions,
|
|
module?: string,
|
|
type: string = "Resource",
|
|
) {
|
|
const providerKey: string = "__provider";
|
|
if (props[providerKey]) {
|
|
throw new Error("A dynamic resource must not define the __provider key");
|
|
}
|
|
props[providerKey] = serializeProvider(provider);
|
|
|
|
super(`pulumi-nodejs:dynamic${module ? `/${module}` : ""}:${type}`, name, props, opts);
|
|
}
|
|
}
|