Merge pull request #412 from pulumi/DynamicResources
Implement dynamic resources.
This commit is contained in:
commit
5fce66ba44
18 changed files with 202 additions and 180 deletions
85
examples/dynamic-provider/simple/index.ts
Normal file
85
examples/dynamic-provider/simple/index.ts
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
||||
|
||||
import * as pulumi from "pulumi";
|
||||
import * as dynamic from "pulumi/dynamic";
|
||||
|
||||
class OperatorProvider implements dynamic.ResourceProvider {
|
||||
private op: (l: number, r: number) => any;
|
||||
|
||||
constructor(op: (l: number, r: number) => any) {
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
check = (inputs: any) => Promise.resolve(new dynamic.CheckResult(undefined, []));
|
||||
diff = (id: pulumi.ID, olds: any, news: any) => Promise.resolve(new dynamic.DiffResult([], []));
|
||||
delete = (id: pulumi.ID, props: any) => Promise.resolve();
|
||||
|
||||
create = (inputs: any) => Promise.resolve(new dynamic.CreateResult("0", this.op(Number(inputs.left), Number(inputs.right))));
|
||||
|
||||
update = (id: string, olds: any, news: any) => Promise.resolve(new dynamic.UpdateResult(this.op(Number(news.left), Number(news.right))));
|
||||
}
|
||||
|
||||
class DivProvider extends OperatorProvider {
|
||||
constructor() {
|
||||
super((left: number, right: number) => <any>{ quotient: Math.floor(left / right), remainder: left % right });
|
||||
}
|
||||
|
||||
check = (ins: any) => Promise.resolve(new dynamic.CheckResult(undefined, ins.right == 0 ? [ new dynamic.CheckFailure("right", "divisor must be non-zero") ] : []));
|
||||
}
|
||||
|
||||
class Add extends dynamic.Resource {
|
||||
public readonly sum: pulumi.Computed<number>;
|
||||
|
||||
private static provider = new OperatorProvider((left: number, right: number) => <any>{ sum: left + right });
|
||||
|
||||
constructor(name: string, left: pulumi.ComputedValue<number>, right: pulumi.ComputedValue<number>) {
|
||||
super(Add.provider, name, {left: left, right: right, sum: undefined}, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
class Mul extends dynamic.Resource {
|
||||
public readonly product: pulumi.Computed<number>;
|
||||
|
||||
private static provider = new OperatorProvider((left: number, right: number) => <any>{ product: left * right });
|
||||
|
||||
constructor(name: string, left: pulumi.ComputedValue<number>, right: pulumi.ComputedValue<number>) {
|
||||
super(Mul.provider, name, {left: left, right: right, product: undefined}, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
class Sub extends dynamic.Resource {
|
||||
public readonly difference: pulumi.Computed<number>;
|
||||
|
||||
private static provider = new OperatorProvider((left: number, right: number) => <any>{ difference: left - right });
|
||||
|
||||
constructor(name: string, left: pulumi.ComputedValue<number>, right: pulumi.ComputedValue<number>) {
|
||||
super(Sub.provider, name, {left: left, right: right, difference: undefined}, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
class Div extends dynamic.Resource {
|
||||
public readonly quotient: pulumi.Computed<number>;
|
||||
public readonly remainder: pulumi.Computed<number>;
|
||||
|
||||
private static provider = new DivProvider();
|
||||
|
||||
constructor(name: string, left: pulumi.ComputedValue<number>, right: pulumi.ComputedValue<number>) {
|
||||
super(Div.provider, name, {left: left, right: right, quotient: undefined, remainder: undefined}, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
let run = async () => {
|
||||
let config = new pulumi.Config("simple:config");
|
||||
|
||||
let w = Number(config.require("w")), x = Number(config.require("x")), y = Number(config.require("y"));
|
||||
|
||||
let sum = new Add("sum", x, y);
|
||||
let square = new Mul("square", sum.sum, sum.sum);
|
||||
let diff = new Sub("diff", square.product, w);
|
||||
let divrem = new Div("divrem", diff.difference, sum.sum);
|
||||
let result = new Add("result", divrem.quotient, divrem.remainder);
|
||||
|
||||
console.log(`((x + y)^2 - w) / (x + y) + ((x + y)^2 - w) %% (x + y) = ${await result.sum}`);
|
||||
};
|
||||
|
||||
run();
|
|
@ -16,8 +16,7 @@
|
|||
"strictNullChecks": true
|
||||
},
|
||||
"files": [
|
||||
"index.ts",
|
||||
"providers.ts"
|
||||
"index.ts"
|
||||
]
|
||||
}
|
||||
|
|
@ -26,10 +26,9 @@ func TestExamples(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
Dir: path.Join(cwd, "test-provider/simple"),
|
||||
Dir: path.Join(cwd, "dynamic-provider/simple"),
|
||||
Dependencies: []string{"pulumi"},
|
||||
Config: map[string]string{
|
||||
"testing:providers:module": "./bin/providers.js",
|
||||
"simple:config:w": "1",
|
||||
"simple:config:x": "1",
|
||||
"simple:config:y": "1",
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
||||
|
||||
import * as pulumi from "pulumi";
|
||||
|
||||
class Add extends pulumi.CustomResource {
|
||||
public readonly sum: pulumi.Computed<number>;
|
||||
|
||||
constructor(name: string, left: pulumi.ComputedValue<number>, right: pulumi.ComputedValue<number>) {
|
||||
super("testing:simple:add", name, {left: left, right: right, sum: undefined}, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
class Mul extends pulumi.CustomResource {
|
||||
public readonly product: pulumi.Computed<number>;
|
||||
|
||||
constructor(name: string, left: pulumi.ComputedValue<number>, right: pulumi.ComputedValue<number>) {
|
||||
super("testing:simple:mul", name, {left: left, right: right, product: undefined}, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
class Sub extends pulumi.CustomResource {
|
||||
public readonly difference: pulumi.Computed<number>;
|
||||
|
||||
constructor(name: string, left: pulumi.ComputedValue<number>, right: pulumi.ComputedValue<number>) {
|
||||
super("testing:simple:sub", name, {left: left, right: right, difference: undefined}, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
class Div extends pulumi.CustomResource {
|
||||
public readonly quotient: pulumi.Computed<number>;
|
||||
public readonly remainder: pulumi.Computed<number>;
|
||||
|
||||
constructor(name: string, left: pulumi.ComputedValue<number>, right: pulumi.ComputedValue<number>) {
|
||||
super("testing:simple:div", name, {left: left, right: right, quotient: undefined, remainder: undefined}, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
let config = new pulumi.Config("simple:config");
|
||||
|
||||
let w = Number(config.require("w")), x = Number(config.require("x")), y = Number(config.require("y"));
|
||||
|
||||
let sum = new Add("sum", x, y);
|
||||
let square = new Mul("square", sum.sum, sum.sum);
|
||||
let diff = new Sub("diff", square.product, w);
|
||||
let divrem = new Div("divrem", diff.difference, sum.sum);
|
||||
let result = new Add("result", divrem.quotient, divrem.remainder);
|
||||
|
||||
let output = async function(): Promise<void> {
|
||||
console.log(`((x + y)^2 - w) / (x + y) + ((x + y)^2 - w) %% (x + y) = ${await result.sum}`);
|
||||
};
|
||||
|
||||
output();
|
|
@ -1,38 +0,0 @@
|
|||
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
||||
|
||||
import * as pulumi from "pulumi";
|
||||
|
||||
export class Operator implements pulumi.testing.ResourceProvider {
|
||||
private op: (l: number, r: number) => any;
|
||||
|
||||
constructor(op: (l: number, r: number) => any) {
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
check(inputs: any): Promise<pulumi.testing.CheckResult> { return Promise.resolve(new pulumi.testing.CheckResult(undefined, [])); }
|
||||
diff(id: pulumi.ID, olds: any, news: any): Promise<pulumi.testing.DiffResult> { return Promise.resolve(new pulumi.testing.DiffResult([], [])); }
|
||||
delete(id: pulumi.ID, props: any): Promise<void> { return Promise.resolve(); }
|
||||
|
||||
create(inputs: any): Promise<pulumi.testing.CreateResult> {
|
||||
return Promise.resolve(new pulumi.testing.CreateResult("0", this.op(Number(inputs.left), Number(inputs.right))));
|
||||
}
|
||||
|
||||
update(id: string, olds: any, news: any): Promise<pulumi.testing.UpdateResult> {
|
||||
return Promise.resolve(new pulumi.testing.UpdateResult(this.op(Number(news.left), Number(news.right))));
|
||||
}
|
||||
}
|
||||
|
||||
export class Div extends Operator {
|
||||
constructor() {
|
||||
super((left: number, right: number) => <any>{ quotient: Math.floor(left / right), remainder: left % right });
|
||||
}
|
||||
|
||||
check(ins: any): Promise<pulumi.testing.CheckResult> {
|
||||
return Promise.resolve(new pulumi.testing.CheckResult(undefined, ins.right == 0 ? [ new pulumi.testing.CheckFailure("right", "divisor must be non-zero") ] : []));
|
||||
}
|
||||
}
|
||||
|
||||
export var add = new Operator((left: number, right: number) => <any>{ sum: left + right });
|
||||
export var mul = new Operator((left: number, right: number) => <any>{ product: left * right });
|
||||
export var sub = new Operator((left: number, right: number) => <any>{ difference: left - right });
|
||||
export var div = new Div();
|
|
@ -1,7 +0,0 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
typescript@^2.5.3:
|
||||
version "2.5.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.3.tgz#df3dcdc38f3beb800d4bc322646b04a3f6ca7f0d"
|
|
@ -226,7 +226,9 @@ func (rm *resmon) Invoke(ctx context.Context, req *lumirpc.InvokeRequest) (*lumi
|
|||
tok := tokens.ModuleMember(req.GetTok())
|
||||
prov, err := rm.src.plugctx.Host.Provider(tok.Package())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to load resource provider for %v", tok)
|
||||
return nil, err
|
||||
} else if prov == nil {
|
||||
return nil, errors.Errorf("could not load resource provider for package '%v' from $PATH", tok.Package())
|
||||
}
|
||||
|
||||
// Now unpack all of the arguments and prepare to perform the invocation.
|
||||
|
@ -237,7 +239,7 @@ func (rm *resmon) Invoke(ctx context.Context, req *lumirpc.InvokeRequest) (*lumi
|
|||
}
|
||||
|
||||
// Do the invoke and then return the arguments.
|
||||
glog.V(5).Info("ResourceMonitor.Invoke received: tok=%v #args=%v", tok, len(args))
|
||||
glog.V(5).Infof("ResourceMonitor.Invoke received: tok=%v #args=%v", tok, len(args))
|
||||
ret, failures, err := prov.Invoke(tok, args)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invocation of %v returned an error", tok)
|
||||
|
|
3
sdk/nodejs/bin/pulumi-provider-pulumi-nodejs
Executable file
3
sdk/nodejs/bin/pulumi-provider-pulumi-nodejs
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env node
|
||||
require("./cmd/dynamic-provider");
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
require("./cmd/test-provider");
|
||||
|
|
@ -1,64 +1,45 @@
|
|||
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
||||
|
||||
// This is a mock resource provider that can be used to implement custom CRUD operations in JavaScript.
|
||||
// It is configured using a single variable, `pulumi:testing:providers`, that provides the path to the JS
|
||||
// module that implements CRUD operations for various types. When an operation is requested by the engine
|
||||
// for a resource of a particular type, that type's `testing.Provider` is loaded from the input module
|
||||
// using the unqualified type name.
|
||||
|
||||
import * as minimist from "minimist";
|
||||
import * as path from "path";
|
||||
import * as resource from "../../resource";
|
||||
import * as testing from "../../testing";
|
||||
|
||||
import * as dynamic from "../../dynamic";
|
||||
import * as resource from "../../resource";
|
||||
|
||||
const requireFromString = require("require-from-string");
|
||||
const grpc = require("grpc");
|
||||
const emptyproto = require("google-protobuf/google/protobuf/empty_pb.js");
|
||||
const structproto = require("google-protobuf/google/protobuf/struct_pb.js");
|
||||
const provproto = require("../../proto/provider_pb.js");
|
||||
const provrpc = require("../../proto/provider_grpc_pb.js");
|
||||
|
||||
class Providers {
|
||||
private registry: any;
|
||||
const providerKey: string = "__provider";
|
||||
|
||||
constructor(registry: any) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
get(urn: string): testing.ResourceProvider {
|
||||
const urnNameDelimiter: string = "::";
|
||||
const nsDelimiter: string = ":";
|
||||
|
||||
const type = urn.split(urnNameDelimiter)[2].split(nsDelimiter)[2];
|
||||
return this.registry[type];
|
||||
}
|
||||
function getProvider(props: any): dynamic.ResourceProvider {
|
||||
// TODO[pulumi/pulumi#414]: investigate replacing requireFromString with eval
|
||||
return requireFromString(props[providerKey]).handler();
|
||||
}
|
||||
let providers: Providers;
|
||||
|
||||
// Each of the *RPC functions below implements a single method of the resource provider gRPC interface. The CRUD
|
||||
// functions--checkRPC, diffRPC, createRPC, updateRPC, and deleteRPC--all operate in a similar fashion:
|
||||
// 1. Deserialize the dyanmic provider for the resource on which the function is operating
|
||||
// 2. Call the dynamic provider's corresponding {check,diff,create,update,delete} method
|
||||
// 3. Convert and return the results
|
||||
// In all cases, the dynamic provider is available in its serialized form as a property of the resource;
|
||||
// getProvider` is responsible for handling its deserialization. In the case of diffRPC, if the provider itself
|
||||
// has changed, `diff` reports that the resource requires replacement and does not delegate to the dynamic provider.
|
||||
// This allows the creation of the replacement resource to use the new provider while the deletion of the old
|
||||
// resource uses the provider with which it was created.
|
||||
|
||||
function configureRPC(call: any, callback: any): void {
|
||||
try {
|
||||
const req = call.request;
|
||||
|
||||
const variables = req.getVariablesMap();
|
||||
let providersJS = variables.get(testing.ProvidersConfigKey);
|
||||
if (providersJS.startsWith("./") || providersJS.startsWith("../")) {
|
||||
providersJS = path.normalize(path.join(process.cwd(), providersJS));
|
||||
}
|
||||
providers = new Providers(require(providersJS));
|
||||
|
||||
callback(undefined, new emptyproto.Empty());
|
||||
} catch (e) {
|
||||
console.error(new Error().stack);
|
||||
callback(e, undefined);
|
||||
}
|
||||
callback(undefined, new emptyproto.Empty());
|
||||
}
|
||||
|
||||
async function invokeRPC(call: any, callback: any): Promise<void> {
|
||||
const req: any = call.request;
|
||||
const resp = new provproto.InvokeResponse();
|
||||
|
||||
// TODO[pulumi/pulumi#406]: implement this.
|
||||
|
||||
callback(undefined, resp);
|
||||
callback(new Error(`unknown function ${req.getTok()}`), undefined);
|
||||
}
|
||||
|
||||
async function checkRPC(call: any, callback: any): Promise<void> {
|
||||
|
@ -66,7 +47,10 @@ async function checkRPC(call: any, callback: any): Promise<void> {
|
|||
const req: any = call.request;
|
||||
const resp = new provproto.CheckResponse();
|
||||
|
||||
const result = await providers.get(req.getUrn()).check(req.getProperties().toJavaScript());
|
||||
const props = req.getProperties().toJavaScript();
|
||||
const provider = getProvider(props);
|
||||
|
||||
const result = await provider.check(props);
|
||||
if (result.defaults) {
|
||||
resp.setDefaults(structproto.Struct.fromJavaScript(result.defaults));
|
||||
}
|
||||
|
@ -84,7 +68,7 @@ async function checkRPC(call: any, callback: any): Promise<void> {
|
|||
|
||||
callback(undefined, resp);
|
||||
} catch (e) {
|
||||
console.error(new Error().stack);
|
||||
console.error(`${e}: ${e.stack}`);
|
||||
callback(e, undefined);
|
||||
}
|
||||
}
|
||||
|
@ -94,15 +78,25 @@ async function diffRPC(call: any, callback: any): Promise<void> {
|
|||
const req: any = call.request;
|
||||
const resp = new provproto.DiffResponse();
|
||||
|
||||
const result: any = await providers.get(req.getUrn())
|
||||
.diff(req.getId(), req.getOlds().toJavaScript(), req.getNews().toJavaScript());
|
||||
if (result.replaces.length !== 0) {
|
||||
resp.setReplaces(result.replaces);
|
||||
// If the provider itself has changed, do not delegate to the dynamic provider. Instead, simply report that the
|
||||
// resource requires replacement. This allows the new resource to be created using the new provider and the old
|
||||
// resource to be deleted using the old provider.
|
||||
const olds = req.getOlds().toJavaScript();
|
||||
const news = req.getNews().toJavaScript();
|
||||
if (olds[providerKey] !== news[providerKey]) {
|
||||
resp.setReplacesList([ providerKey ]);
|
||||
} else {
|
||||
const provider = getProvider(olds);
|
||||
|
||||
const result: any = await provider.diff(req.getId(), olds, news);
|
||||
if (result.replaces.length !== 0) {
|
||||
resp.setReplacesList(result.replaces);
|
||||
}
|
||||
}
|
||||
|
||||
callback(undefined, resp);
|
||||
} catch (e) {
|
||||
console.error(new Error().stack);
|
||||
console.error(`${e}: ${e.stack}`);
|
||||
callback(e, undefined);
|
||||
}
|
||||
}
|
||||
|
@ -112,7 +106,10 @@ async function createRPC(call: any, callback: any): Promise<void> {
|
|||
const req: any = call.request;
|
||||
const resp = new provproto.CreateResponse();
|
||||
|
||||
const result = await providers.get(req.getUrn()).create(req.getProperties().toJavaScript());
|
||||
const props = req.getProperties().toJavaScript();
|
||||
const provider = getProvider(props);
|
||||
|
||||
const result = await provider.create(props);
|
||||
resp.setId(result.id);
|
||||
if (result.outs) {
|
||||
resp.setProperties(structproto.Struct.fromJavaScript(result.outs));
|
||||
|
@ -120,7 +117,7 @@ async function createRPC(call: any, callback: any): Promise<void> {
|
|||
|
||||
callback(undefined, resp);
|
||||
} catch (e) {
|
||||
console.error(new Error().stack);
|
||||
console.error(`${e}: ${e.stack}`);
|
||||
callback(e, undefined);
|
||||
}
|
||||
}
|
||||
|
@ -130,15 +127,21 @@ async function updateRPC(call: any, callback: any): Promise<void> {
|
|||
const req: any = call.request;
|
||||
const resp = new provproto.UpdateResponse();
|
||||
|
||||
const result: any = await providers.get(req.getUrn())
|
||||
.update(req.getId(), req.getOlds().toJavaScript(), req.getNews().toJavaScript());
|
||||
const olds = req.getOlds().toJavaScript();
|
||||
const news = req.getNews().toJavaScript();
|
||||
if (olds[providerKey] !== news[providerKey]) {
|
||||
throw new Error("changes to provider should require replacement");
|
||||
}
|
||||
const provider = getProvider(olds);
|
||||
|
||||
const result: any = await provider.update(req.getId(), olds, news);
|
||||
if (result.outs) {
|
||||
resp.setProperties(structproto.Struct.fromJavaScript(result.outs));
|
||||
}
|
||||
|
||||
callback(undefined, resp);
|
||||
} catch (e) {
|
||||
console.error(new Error().stack);
|
||||
console.error(`${e}: ${e.stack}`);
|
||||
callback(e, undefined);
|
||||
}
|
||||
}
|
||||
|
@ -146,10 +149,11 @@ async function updateRPC(call: any, callback: any): Promise<void> {
|
|||
async function deleteRPC(call: any, callback: any): Promise<void> {
|
||||
try {
|
||||
const req: any = call.request;
|
||||
await providers.get(req.getUrn()).delete(req.getId(), req.getProperties());
|
||||
const props: any = req.getProperties().toJavaScript();
|
||||
await getProvider(props).delete(req.getId(), props);
|
||||
callback(undefined, new emptyproto.Empty());
|
||||
} catch (e) {
|
||||
console.error(new Error().stack);
|
||||
console.error(`${e}: ${e.stack}`);
|
||||
callback(e, undefined);
|
||||
}
|
||||
}
|
|
@ -1,13 +1,7 @@
|
|||
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
||||
|
||||
import * as resource from "../resource";
|
||||
|
||||
/**
|
||||
* ProvidersConfigKey is the configuration key used to provide the testing provider with the path to a JavaScript module
|
||||
* that exports a map from (unqualified) type names to `ResourceProvider`s. This map is then used to decide which
|
||||
* `ResourceProvider` should be used to implement the CRUD operations for a particular resource type.
|
||||
*/
|
||||
export const ProvidersConfigKey = "testing:providers:module"
|
||||
import * as resource from "./resource";
|
||||
import * as runtime from "./runtime";
|
||||
|
||||
/**
|
||||
* CheckResult represents the results of a call to `ResourceProvider.check`.
|
||||
|
@ -123,7 +117,7 @@ export class UpdateResult {
|
|||
public readonly outs: any | undefined;
|
||||
|
||||
/**
|
||||
* Constructs a new udpate result.
|
||||
* Constructs a new update result.
|
||||
*
|
||||
* @param outs Any properties that were computed during updating.
|
||||
*/
|
||||
|
@ -133,7 +127,7 @@ export class UpdateResult {
|
|||
}
|
||||
|
||||
/**
|
||||
* ResourceProvider represents an object that provides CRUD operations for a particular
|
||||
* ResourceProvider represents an object that provides CRUD operations for a particular type of resource.
|
||||
*/
|
||||
export interface ResourceProvider {
|
||||
/**
|
||||
|
@ -141,7 +135,7 @@ export interface ResourceProvider {
|
|||
*
|
||||
* @param inputs The full properties to use for validation.
|
||||
*/
|
||||
check(inputs: any): Promise<CheckResult>;
|
||||
check: (inputs: any) => Promise<CheckResult>;
|
||||
|
||||
/**
|
||||
* Diff checks what impacts a hypothetical update will have on the resource's properties.
|
||||
|
@ -150,7 +144,7 @@ export interface ResourceProvider {
|
|||
* @param olds The old values of properties to diff.
|
||||
* @param news The new values of properties to diff.
|
||||
*/
|
||||
diff(id: resource.ID, olds: any, news: any): Promise<DiffResult>;
|
||||
diff: (id: resource.ID, olds: any, news: any) => Promise<DiffResult>;
|
||||
|
||||
/**
|
||||
* Create allocates a new instance of the provided resource and returns its unique ID afterwards.
|
||||
|
@ -158,7 +152,7 @@ export interface ResourceProvider {
|
|||
*
|
||||
* @param inputs The properties to set during creation.
|
||||
*/
|
||||
create(inputs: any): Promise<CreateResult>;
|
||||
create: (inputs: any) => Promise<CreateResult>;
|
||||
|
||||
/**
|
||||
* Update updates an existing resource with new values.
|
||||
|
@ -167,7 +161,7 @@ export interface ResourceProvider {
|
|||
* @param olds The old values of properties to update.
|
||||
* @param news The new values of properties to update.
|
||||
*/
|
||||
update(id: resource.ID, olds: any, news: any): Promise<UpdateResult>;
|
||||
update: (id: resource.ID, olds: any, news: any) => Promise<UpdateResult>;
|
||||
|
||||
/**
|
||||
* Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
|
@ -175,5 +169,37 @@ export interface ResourceProvider {
|
|||
* @param id The ID of the resource to delete.
|
||||
* @param props The current properties on the resource.
|
||||
*/
|
||||
delete(id: resource.ID, props: any): Promise<void>;
|
||||
delete: (id: resource.ID, props: any) => Promise<void>;
|
||||
}
|
||||
|
||||
async function serializeProvider(provider: ResourceProvider): Promise<string> {
|
||||
return runtime.serializeJavaScriptText(await runtime.serializeClosure(() => provider));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 dependsOn Optional additional explicit dependencies on other resources.
|
||||
*/
|
||||
public constructor(provider: ResourceProvider,
|
||||
name: string,
|
||||
props: resource.ComputedValues,
|
||||
dependsOn?: resource.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:Resource", name, props, dependsOn);
|
||||
}
|
||||
}
|
|
@ -9,8 +9,8 @@ export * from "./resource";
|
|||
|
||||
// Export submodules individually.
|
||||
import * as asset from "./asset";
|
||||
import * as dynamic from "./dynamic";
|
||||
import * as log from "./log";
|
||||
import * as runtime from "./runtime";
|
||||
import * as testing from "./testing";
|
||||
export { asset, log, runtime, testing };
|
||||
export { asset, dynamic, log, runtime };
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"google-protobuf": "^3.4.0",
|
||||
"grpc": "^1.6.0",
|
||||
"minimist": "^1.2.0",
|
||||
"require-from-string": "^2.0.1",
|
||||
"source-map-support": "^0.4.16"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
"files": [
|
||||
"index.ts",
|
||||
"config.ts",
|
||||
"dynamic.ts",
|
||||
"errors.ts",
|
||||
"resource.ts",
|
||||
|
||||
|
@ -37,11 +38,9 @@
|
|||
"runtime/rpc.ts",
|
||||
"runtime/settings.ts",
|
||||
|
||||
"cmd/dynamic-provider/index.ts",
|
||||
"cmd/langhost/index.ts",
|
||||
"cmd/run/index.ts",
|
||||
"cmd/test-provider/index.ts",
|
||||
|
||||
"testing/index.ts",
|
||||
|
||||
"tests/config.spec.ts",
|
||||
"tests/init.spec.ts",
|
||||
|
|
|
@ -1010,6 +1010,10 @@ request@2, request@^2.81.0:
|
|||
tunnel-agent "^0.6.0"
|
||||
uuid "^3.0.0"
|
||||
|
||||
require-from-string@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.1.tgz#c545233e9d7da6616e9d59adfb39fc9f588676ff"
|
||||
|
||||
resolve@1.1.x:
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
|
||||
|
|
Loading…
Reference in a new issue