Merge branch 'master' into gometalinter

This commit is contained in:
Britton Forsyth 2017-06-09 14:34:51 -07:00 committed by GitHub
commit 69e4834f63
95 changed files with 1814 additions and 871 deletions

View file

@ -21,13 +21,6 @@ banner_all:
@echo "\033[1;37mLumi (Full)\033[0m"
@echo "\033[1;37m============\033[0m"
.PHONY: build
build:
@echo "\033[0;32mBUILD:\033[0m"
@go version
@go build ${PROJECT}/cmd/lumi
@go build ${PROJECT}/cmd/lumidl
.PHONY: install
install:
@echo "\033[0;32mINSTALL:\033[0m"

View file

@ -1023,11 +1023,14 @@ export class Transformer {
contract.assert(!!idsym, `Expected an ID symbol for '${id.ident}', but it is missing`);
tok = await this.resolveTokenFromSymbol(idsym);
// note that we intentionally leave object blank, since the token is fully qualified.
if ((idsym.flags & ts.SymbolFlags.Alias) === 0) {
// Mark as dynamic unless this is an alias to a module import.
isDynamic = true;
}
}
if (isDynamic) {
// If the target type is `dynamic`, we cannot perform static lookups; devolve into a dynamic load.
contract.assert(!!object);
return this.withLocation(node, <ast.TryLoadDynamicExpression>{
kind: ast.tryLoadDynamicExpressionKind,
object: object,

View file

@ -1215,10 +1215,10 @@
"right": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a2",
"kind": "StringLiteral",
"value": "a2",
"loc": {
"file": "index.ts",
"start": {
@ -1303,10 +1303,10 @@
"left": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a2",
"kind": "StringLiteral",
"value": "a2",
"loc": {
"file": "index.ts",
"start": {
@ -1436,10 +1436,10 @@
"right": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a2",
"kind": "StringLiteral",
"value": "a2",
"loc": {
"file": "index.ts",
"start": {
@ -1524,10 +1524,10 @@
"left": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a2",
"kind": "StringLiteral",
"value": "a2",
"loc": {
"file": "index.ts",
"start": {
@ -1657,10 +1657,10 @@
"right": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a2",
"kind": "StringLiteral",
"value": "a2",
"loc": {
"file": "index.ts",
"start": {
@ -1745,10 +1745,10 @@
"left": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a2",
"kind": "StringLiteral",
"value": "a2",
"loc": {
"file": "index.ts",
"start": {
@ -2004,10 +2004,10 @@
"right": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a3",
"kind": "StringLiteral",
"value": "a3",
"loc": {
"file": "index.ts",
"start": {
@ -2121,10 +2121,10 @@
"right": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a3",
"kind": "StringLiteral",
"value": "a3",
"loc": {
"file": "index.ts",
"start": {
@ -2238,10 +2238,10 @@
"right": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a3",
"kind": "StringLiteral",
"value": "a3",
"loc": {
"file": "index.ts",
"start": {
@ -2859,10 +2859,10 @@
"right": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -2978,10 +2978,10 @@
"object": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -3125,10 +3125,10 @@
"object": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -3272,10 +3272,10 @@
"object": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -3417,10 +3417,10 @@
"right": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -3536,10 +3536,10 @@
"object": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -3683,10 +3683,10 @@
"object": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -3830,10 +3830,10 @@
"object": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -4189,10 +4189,10 @@
"right": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -4308,10 +4308,10 @@
"object": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -4455,10 +4455,10 @@
"object": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -4602,10 +4602,10 @@
"object": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -4747,10 +4747,10 @@
"right": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -4866,10 +4866,10 @@
"object": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -5013,10 +5013,10 @@
"object": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {
@ -5160,10 +5160,10 @@
"object": {
"kind": "TryLoadDynamicExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/arrays:index:a6",
"kind": "StringLiteral",
"value": "a6",
"loc": {
"file": "index.ts",
"start": {

View file

@ -942,10 +942,10 @@
"expression": {
"kind": "InvokeFunctionExpression",
"function": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/undefined:index:f",
"kind": "StringLiteral",
"value": "f",
"loc": {
"file": "index.ts",
"start": {
@ -1237,10 +1237,10 @@
"expression": {
"kind": "InvokeFunctionExpression",
"function": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/undefined:index:g",
"kind": "StringLiteral",
"value": "g",
"loc": {
"file": "index.ts",
"start": {

View file

@ -1023,10 +1023,10 @@
},
"operator": "=",
"right": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/props:index:modprop",
"kind": "StringLiteral",
"value": "modprop",
"loc": {
"file": "index.ts",
"start": {
@ -1256,10 +1256,10 @@
"kind": "BinaryOperatorExpression",
"operator": "!=",
"left": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/props:index:c",
"kind": "StringLiteral",
"value": "c",
"loc": {
"file": "index.ts",
"start": {
@ -1389,10 +1389,10 @@
"right": {
"kind": "LoadLocationExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/props:index:c",
"kind": "StringLiteral",
"value": "c",
"loc": {
"file": "index.ts",
"start": {
@ -1568,10 +1568,10 @@
"tok": "f"
},
"value": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/props:index:modprop",
"kind": "StringLiteral",
"value": "modprop",
"loc": {
"file": "index.ts",
"start": {
@ -1664,10 +1664,10 @@
"value": {
"kind": "LoadLocationExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/props:index:c",
"kind": "StringLiteral",
"value": "c",
"loc": {
"file": "index.ts",
"start": {
@ -1770,10 +1770,10 @@
"property": {
"kind": "InvokeFunctionExpression",
"function": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/props:index:j",
"kind": "StringLiteral",
"value": "j",
"loc": {
"file": "index.ts",
"start": {
@ -1965,10 +1965,10 @@
"right": {
"kind": "LoadLocationExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/props:index:c",
"kind": "StringLiteral",
"value": "c",
"loc": {
"file": "index.ts",
"start": {
@ -2065,10 +2065,10 @@
"left": {
"kind": "LoadLocationExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/props:index:c",
"kind": "StringLiteral",
"value": "c",
"loc": {
"file": "index.ts",
"start": {

View file

@ -1164,10 +1164,10 @@
"function": {
"kind": "LoadLocationExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/super:index:b",
"kind": "StringLiteral",
"value": "b",
"loc": {
"file": "index.ts",
"start": {
@ -1262,10 +1262,10 @@
"kind": "BinaryOperatorExpression",
"operator": "!=",
"left": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/super:index:bgx",
"kind": "StringLiteral",
"value": "bgx",
"loc": {
"file": "index.ts",
"start": {
@ -1363,10 +1363,10 @@
}
},
"right": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/super:index:bgx",
"kind": "StringLiteral",
"value": "bgx",
"loc": {
"file": "index.ts",
"start": {
@ -1581,10 +1581,10 @@
"function": {
"kind": "LoadLocationExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/super:index:c",
"kind": "StringLiteral",
"value": "c",
"loc": {
"file": "index.ts",
"start": {
@ -1679,10 +1679,10 @@
"kind": "BinaryOperatorExpression",
"operator": "!=",
"left": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/super:index:cgx",
"kind": "StringLiteral",
"value": "cgx",
"loc": {
"file": "index.ts",
"start": {
@ -1780,10 +1780,10 @@
}
},
"right": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/super:index:cgx",
"kind": "StringLiteral",
"value": "cgx",
"loc": {
"file": "index.ts",
"start": {
@ -1910,10 +1910,10 @@
"function": {
"kind": "LoadLocationExpression",
"object": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/super:index:c",
"kind": "StringLiteral",
"value": "c",
"loc": {
"file": "index.ts",
"start": {
@ -2008,10 +2008,10 @@
"kind": "BinaryOperatorExpression",
"operator": "!=",
"left": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/super:index:cgy",
"kind": "StringLiteral",
"value": "cgy",
"loc": {
"file": "index.ts",
"start": {
@ -2109,10 +2109,10 @@
}
},
"right": {
"kind": "LoadLocationExpression",
"kind": "TryLoadDynamicExpression",
"name": {
"kind": "Token",
"tok": "basic/super:index:cgy",
"kind": "StringLiteral",
"value": "cgy",
"loc": {
"file": "index.ts",
"start": {

View file

@ -4,6 +4,6 @@
set -e # bail on errors
echo Compiling:
go build -o lumi-analyzer-contoso_infosec
go build -i -o lumi-analyzer-contoso_infosec
echo Done.

View file

@ -1,4 +1,3 @@
.lumi/
bin/
node_modules/

View file

@ -1,6 +1,6 @@
{
"name": "beanstalk",
"main": ".lumi/bin/index.js",
"version": "0.1",
"typings": ".lumi/bin/index.d.ts",
"scripts": {
"build": "lumijs"

View file

@ -1,4 +1,3 @@
.lumi/
bin/
node_modules/

View file

@ -1,7 +1,7 @@
{
"name": "cpuwatch",
"main": "bin/index.js",
"typings": "bin/index.d.ts",
"version": "0.1",
"typings": ".lumi/bin/index.d.ts",
"scripts": {
"build": "lumijs"
},

View file

@ -1,3 +1,3 @@
bin/
.lumi/
node_modules/

View file

@ -1,5 +1,5 @@
name: aws/minimal
description: A minimal AWS blueprint that consists solely of creating a single VPC resource.
description: An empty Lumi script.
dependencies:
lumi: "*"
aws: "*"

View file

@ -1,4 +1,3 @@
import * as lumi from "@lumi/lumi";
import * as aws from "@lumi/aws";
let vpc = new aws.ec2.VPC({ cidrBlock: "10.0.0.0/16" });

View file

@ -0,0 +1,16 @@
{
"name": "minimal",
"version": "0.1",
"typings": ".lumi/bin/index.d.ts",
"scripts": {
"build": "lumijs"
},
"devDependencies": {
"typescript": "^2.1.4"
},
"peerDependencies": {
"@lumi/lumi": "*",
"@lumi/aws": "*"
}
}

View file

@ -1,6 +1,6 @@
{
"compilerOptions": {
"outDir": "bin",
"outDir": ".lumi/bin",
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",

View file

@ -16,7 +16,9 @@
"strictNullChecks": true
},
"files": [
"index.ts"
"index.ts",
"infra.ts",
"swarm.ts"
]
}

View file

@ -1,4 +1,3 @@
.lumi/
bin/
node_modules/

View file

@ -2,5 +2,6 @@ name: serverless
description: Basic example of a serverless AWS application.
dependencies:
lumi: "*"
lumijs: "*"
aws: "*"

View file

@ -49,34 +49,17 @@ let music = new aws.dynamodb.Table("music", {
],
})
// TODO[pulumi/lumi#174] Until we have global definitions available in Lumi for these APIs that are expected
// by runtime code, we'll declare variables that should be available on the global scope of the lambda to keep
// TypeScript type checking happy.
let console: any
function createLambda() {
// TODO[pulumi/lumi#175] Currently, we can only capture local variables, not module scope variables,
// so we keep this inside a helper function.
let hello = "Hello, world!"
let num = 3
let obj = { x: 42 }
let mus = music
let lambda = new aws.serverless.Function(
"mylambda",
[aws.iam.AWSLambdaFullAccess],
(event, context, callback) => {
console.log(hello);
console.log(obj.x);
console.log("Music table hash key is: " + mus.hashKey);
console.log("Invoked function: " + context.invokedFunctionArn);
callback(null, "Succeeed with " + context.getRemainingTimeInMillis() + "ms remaining.");
}
);
return lambda;
}
let lambda = createLambda();
let hello = "Hello, world!"
let lambda = new aws.serverless.Function(
"mylambda",
[aws.iam.AWSLambdaFullAccess],
(event, context, callback) => {
console.log(hello);
console.log("Music table hash key is: " + music.hashKey);
console.log("Invoked function: " + context.invokedFunctionArn);
callback(null, "Succeeed with " + context.getRemainingTimeInMillis() + "ms remaining.");
}
);
let api = new aws.serverless.API("frontend")
api.route("GET", "/bambam", lambda)

View file

@ -1,7 +1,7 @@
{
"name": "serverless",
"main": "bin/index.js",
"typings": "bin/index.d.ts",
"version": "0.1",
"typings": ".lumi/bin/index.d.ts",
"scripts": {
"build": "lumijs"
},
@ -9,6 +9,7 @@
"typescript": "^2.1.4"
},
"peerDependencies": {
"@lumi/lumi": "*",
"@lumi/aws": "*"
}
}

View file

@ -1,4 +1,3 @@
.lumi/
bin/
node_modules/

View file

@ -1,7 +1,7 @@
{
"name": "webserver",
"main": "bin/index.js",
"typings": "bin/index.d.ts",
"version": "0.1",
"typings": ".lumi/bin/index.d.ts",
"scripts": {
"build": "lumijs"
},

View file

@ -1,4 +1,3 @@
.lumi/
bin/
node_modules/

View file

@ -1,7 +1,7 @@
{
"name": "webserver",
"main": "bin/index.js",
"typings": "bin/index.d.ts",
"version": "0.1",
"typings": ".lumi/bin/index.d.ts",
"scripts": {
"build": "lumijs"
},

View file

@ -34,7 +34,7 @@ build:
@cd pack/ && lumi pack verify # ensure the pack verifies
@cp -R pack/.lumi/bin/ bin/ # copy the pack to our bin dir
@go version
@cd provider/ && go build -o ../bin/lumi-resource-aws # compile the resource provider
@cd provider/ && go build -i -o ../bin/lumi-resource-aws # compile the resource provider
.PHONY: install
install:

View file

@ -13,12 +13,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/* tslint:disable: ordered-imports */
import { jsonStringify, sha1hash, printf } from "@lumi/lumi/runtime";
import { Deployment, RestAPI, Stage } from "../apigateway";
import { Permission } from "../lambda";
import { Function } from "./function";
import { region } from "../config";
export interface Route {
method: string;
path: string;
@ -75,6 +78,10 @@ function createPathSpec(lambdaARN: string): SwaggerOperation {
};
}
function createSourceARN(region: string, account: string, apiid: string, functionName: string): string {
return "arn:aws:execute-api:"+region+":"+account+":"+apiid+"/*/*/"+ functionName;
}
// API is a higher level abstraction for working with AWS APIGateway reources.
export class API {
public api: RestAPI;
@ -111,7 +118,19 @@ export class API {
default:
throw new Error("Method not supported: " + method);
}
let apiName = "";
if(this.api.apiName !== undefined) {
apiName = this.api.apiName;
}
let invokePermission = new Permission(this.apiName + "_invoke_" + sha1hash(method + path), {
action: "lambda:invokeFunction",
function: lambda.lambda,
principal: "apigateway.amazonaws.com",
sourceARN: createSourceARN("us-east-1", "490047557317", apiName, "webapi-test-func"),
});
// TODO[pulumi/lumi#90]: Once we suport output properties, we can use `lambda.lambda.arn` as input
// to constructing this apigateway lambda invocation uri.
// this.swaggerSpec.paths[path][swaggerMethod] = createPathSpec(lambda.lambda.arn);
this.swaggerSpec.paths[path][swaggerMethod] = createPathSpec(

View file

@ -103,8 +103,8 @@ func (arn ARN) Parse() (Parts, error) {
if len(ps) > 5 {
parts.Resource = ps[5]
}
if len(ps) > 6 {
parts.Resource = parts.Resource + ":" + ps[6]
for i := 6; i < len(ps); i++ {
parts.Resource = parts.Resource + ":" + ps[i]
}
return parts, nil
}

View file

@ -29,12 +29,12 @@ func Test(t *testing.T) {
cleanupFunctions(ctx)
cleanupRoles(ctx)
functionProvider := NewFunctionProvider(ctx)
roleProvider := iamprovider.NewRoleProvider(ctx)
sourceARN := rpc.ARN("arn:aws:s3:::elasticbeanstalk-us-east-1-111111111111")
resources := map[string]testutil.Resource{
"role": {Provider: roleProvider, Token: iam.RoleToken},
"f": {Provider: functionProvider, Token: FunctionToken},
"role": {Provider: iamprovider.NewRoleProvider(ctx), Token: iam.RoleToken},
"f": {Provider: NewFunctionProvider(ctx), Token: FunctionToken},
"permission": {Provider: NewPermissionProvider(ctx), Token: PermissionToken},
}
steps := []testutil.Step{
testutil.Step{
@ -80,6 +80,19 @@ func Test(t *testing.T) {
}
},
},
testutil.ResourceGenerator{
Name: "permission",
Creator: func(ctx testutil.Context) interface{} {
return &lambda.Permission{
Name: aws.String(RESOURCEPREFIX),
Function: ctx.GetResourceID("f"),
Action: "lambda:InvokeFunction",
Principal: "s3.amazonaws.com",
SourceAccount: aws.String("111111111111"),
SourceARN: &sourceARN,
}
},
},
},
}

View file

@ -0,0 +1,237 @@
// Licensed to Pulumi Corporation ("Pulumi") under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// Pulumi licenses this file to You 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.
package lambda
import (
"crypto/sha1"
"encoding/json"
"fmt"
"reflect"
"regexp"
"strings"
"github.com/aws/aws-sdk-go/aws"
awslambda "github.com/aws/aws-sdk-go/service/lambda"
"github.com/pulumi/lumi/pkg/resource"
"github.com/pulumi/lumi/pkg/util/contract"
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
"golang.org/x/net/context"
"github.com/pulumi/lumi/lib/aws/provider/arn"
"github.com/pulumi/lumi/lib/aws/provider/awsctx"
awscommon "github.com/pulumi/lumi/lib/aws/rpc"
"github.com/pulumi/lumi/lib/aws/rpc/lambda"
)
const PermissionToken = lambda.PermissionToken
const (
maxStatementID = 100
actionRegexp = "(lambda:[*]|lambda:[a-zA-Z]+|[*])"
sourceAccountRegexp = "\\d{12}"
sourceARNRegexp = "arn:aws:([a-zA-Z0-9\\-])+:([a-z]{2}-[a-z]+-\\d{1})?:(\\d{12})?:(.*)"
)
type policy struct {
Version string
ID string `json:"Id"`
Statement []statement
}
type statement struct {
Sid string
Effect string
Principal principal
Action string
Resource string
Condition condition
}
type principal struct {
Service string
}
type condition struct {
ArnLike *arnLike `json:"ArnLike,omitempty"`
StringEquals *stringEquals `json:"StringEquals,omitempty"`
}
type arnLike struct {
AWSSourceArn *string `json:"AWS:SourceArn,omitempty"`
}
type stringEquals struct {
AWSSourceAccount *string `json:"AWS:SourceAccount,omitempty"`
}
// NewPermissionID returns an AWS APIGateway Deployment ARN ID for the given restAPIID and deploymentID
func NewPermissionID(region, account, functionName, statementID string) resource.ID {
return arn.NewID("lambda", region, account, "function:"+functionName+":policy:"+statementID)
}
// ParsePermissionID parses an AWS APIGateway Deployment ARN ID to extract the restAPIID and deploymentID
func ParsePermissionID(id resource.ID) (string, string, error) {
res, err := arn.ParseResourceName(id)
if err != nil {
return "", "", err
}
parts := strings.Split(res, ":")
if len(parts) != 3 || parts[1] != "policy" {
return "", "", fmt.Errorf("expected Permission ARN of the form %v: %v",
"arn:aws:lambda:region:account:function:function-name:policy:statement-id", id)
}
return parts[0], parts[2], nil
}
// NewPermissionProvider creates a provider that handles Lambda permission operations.
func NewPermissionProvider(ctx *awsctx.Context) lumirpc.ResourceProviderServer {
ops := &permissionProvider{ctx}
return lambda.NewPermissionProvider(ops)
}
type permissionProvider struct {
ctx *awsctx.Context
}
// Check validates that the given property bag is valid for a resource of the given type.
func (p *permissionProvider) Check(ctx context.Context, obj *lambda.Permission) ([]error, error) {
var failures []error
if matched, err := regexp.MatchString(actionRegexp, obj.Action); err != nil || !matched {
failures = append(failures,
resource.NewFieldError(reflect.TypeOf(obj), lambda.Permission_Action,
fmt.Errorf("did not match regexp %v", actionRegexp)))
}
if obj.SourceAccount != nil {
if matched, err := regexp.MatchString(sourceAccountRegexp, *obj.SourceAccount); err != nil || !matched {
fmt.Printf("adding failure because source account didn't match\n")
failures = append(failures,
resource.NewFieldError(reflect.TypeOf(obj), lambda.Permission_SourceAccount,
fmt.Errorf("did not match regexp %v", sourceAccountRegexp)))
}
}
if obj.SourceARN != nil {
if matched, err := regexp.MatchString(sourceARNRegexp, string(*obj.SourceARN)); err != nil || !matched {
failures = append(failures,
resource.NewFieldError(reflect.TypeOf(obj), lambda.Permission_SourceARN,
fmt.Errorf("did not match regexp %v", sourceARNRegexp)))
}
}
return failures, nil
}
// Create allocates a new instance of the provided resource and returns its unique ID afterwards. (The input ID
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
func (p *permissionProvider) Create(ctx context.Context, obj *lambda.Permission) (resource.ID, error) {
// Auto-generate a name in part based on the resource name.
statementID := resource.NewUniqueHex(*obj.Name+"-", maxStatementID, sha1.Size)
functionName, err := arn.ParseResourceName(obj.Function)
if err != nil {
return "", err
}
fmt.Printf("Creating Lambda Permission '%v' with statement ID '%v'\n", *obj.Name, statementID)
create := &awslambda.AddPermissionInput{
Action: aws.String(obj.Action),
FunctionName: aws.String(functionName),
Principal: aws.String(obj.Principal),
SourceAccount: obj.SourceAccount,
StatementId: aws.String(statementID),
}
if obj.SourceARN != nil {
create.SourceArn = aws.String(string(*obj.SourceARN))
}
_, err = p.ctx.Lambda().AddPermission(create)
if err != nil {
return "", err
}
return NewPermissionID(p.ctx.Region(), p.ctx.AccountID(), functionName, statementID), nil
}
// Get reads the instance state identified by ID, returning a populated resource object, or an error if not found.
func (p *permissionProvider) Get(ctx context.Context, id resource.ID) (*lambda.Permission, error) {
functionName, statementID, err := ParsePermissionID(id)
if err != nil {
return nil, err
}
resp, err := p.ctx.Lambda().GetPolicy(&awslambda.GetPolicyInput{
FunctionName: aws.String(functionName),
})
if err != nil {
return nil, err
}
contract.Assert(resp != nil)
contract.Assert(resp.Policy != nil)
policy := policy{}
err = json.Unmarshal([]byte(*resp.Policy), &policy)
if err != nil {
return nil, err
}
for _, statement := range policy.Statement {
if statement.Sid == statementID {
permission := &lambda.Permission{
Action: statement.Action,
Function: resource.ID(statement.Resource),
Principal: statement.Principal.Service,
}
// The statements generated by `lambda.AddPermission` will contain up to two Condition elements
// of the following two forms, corresponding to the optional SourceARN and SourceAccount properites.
// "ArnLike": { "AWS:SourceArn": "<arn>" }
// or:
// "StringEquals": { "AWS:SourceAccount": "<account-id>" }
condition := statement.Condition
if condition.ArnLike != nil && condition.ArnLike.AWSSourceArn != nil {
sourceARN := awscommon.ARN(*condition.ArnLike.AWSSourceArn)
permission.SourceARN = &sourceARN
}
if condition.StringEquals != nil && condition.StringEquals.AWSSourceAccount != nil {
permission.SourceAccount = condition.StringEquals.AWSSourceAccount
}
return permission, nil
}
}
return nil, fmt.Errorf("No statement found for id '%v'", id)
}
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
func (p *permissionProvider) InspectChange(ctx context.Context, id resource.ID,
old *lambda.Permission, new *lambda.Permission, diff *resource.ObjectDiff) ([]string, error) {
return nil, nil
}
// Update updates an existing resource with new values. Only those values in the provided property bag are updated
// to new values. The resource ID is returned and may be different if the resource had to be recreated.
func (p *permissionProvider) Update(ctx context.Context, id resource.ID,
old *lambda.Permission, new *lambda.Permission, diff *resource.ObjectDiff) error {
contract.Failf("No properties of Permission resource are updatable.")
return nil
}
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
func (p *permissionProvider) Delete(ctx context.Context, id resource.ID) error {
functionName, statementID, err := ParsePermissionID(id)
if err != nil {
return err
}
fmt.Printf("Deleting Lambda Permission '%v'\n", statementID)
_, err = p.ctx.Lambda().RemovePermission(&awslambda.RemovePermissionInput{
FunctionName: aws.String(functionName),
StatementId: aws.String(statementID),
})
if err != nil {
return err
}
return nil
}

View file

@ -56,6 +56,7 @@ func NewProvider() (*Provider, error) {
elasticbeanstalk.ApplicationVersionToken: elasticbeanstalk.NewApplicationVersionProvider(ctx),
elasticbeanstalk.EnvironmentToken: elasticbeanstalk.NewEnvironmentProvider(ctx),
lambda.FunctionToken: lambda.NewFunctionProvider(ctx),
lambda.PermissionToken: lambda.NewPermissionProvider(ctx),
iam.RoleToken: iam.NewRoleProvider(ctx),
s3.BucketToken: s3.NewBucketProvider(ctx),
s3.ObjectToken: s3.NewObjectProvider(ctx),

View file

@ -1,6 +1,7 @@
package testutil
import (
"fmt"
"testing"
structpb "github.com/golang/protobuf/ptypes/struct"
@ -35,18 +36,25 @@ type Step []ResourceGenerator
func ProviderTest(t *testing.T, resources map[string]Resource, steps []Step) {
p := &providerTest{
resources: resources,
ids: map[string]resource.ID{},
props: map[string]*structpb.Struct{},
resources: resources,
namesInCreationOrder: []string{},
ids: map[string]resource.ID{},
props: map[string]*structpb.Struct{},
}
// For each step, create or update all listed resources
for _, step := range steps {
for _, res := range step {
provider := resources[res.Name].Provider
token := resources[res.Name].Token
currentResource, ok := resources[res.Name]
if !ok {
t.Fatalf("expected resource to have been pre-declared: %v", res.Name)
}
provider := currentResource.Provider
token := currentResource.Token
if id, ok := p.ids[res.Name]; !ok {
id, props := createResource(t, res.Creator(p), provider, token)
p.ids[res.Name] = resource.ID(id)
p.namesInCreationOrder = append(p.namesInCreationOrder, res.Name)
p.props[res.Name] = props
if id == "" {
t.Fatal("expected to succesfully create resource")
@ -61,7 +69,10 @@ func ProviderTest(t *testing.T, resources map[string]Resource, steps []Step) {
}
}
}
for name, id := range p.ids {
// Delete resources in the opposite order they were created
for i := len(p.namesInCreationOrder) - 1; i >= 0; i-- {
name := p.namesInCreationOrder[i]
id := p.ids[name]
provider := resources[name].Provider
token := resources[name].Token
ok := deleteResource(t, string(id), provider, token)
@ -97,9 +108,10 @@ func ProviderTestSimple(t *testing.T, provider lumirpc.ResourceProviderServer, t
}
type providerTest struct {
resources map[string]Resource
ids map[string]resource.ID
props map[string]*structpb.Struct
resources map[string]Resource
namesInCreationOrder []string
ids map[string]resource.ID
props map[string]*structpb.Struct
}
func (p *providerTest) GetResourceID(name string) resource.ID {
@ -113,6 +125,7 @@ var _ Context = &providerTest{}
func createResource(t *testing.T, res interface{}, provider lumirpc.ResourceProviderServer, token tokens.Type) (string, *structpb.Struct) {
props := resource.MarshalProperties(nil, resource.NewPropertyMap(res), resource.MarshalOptions{})
fmt.Printf("[Provider Test]: Checking %v\n", token)
checkResp, err := provider.Check(nil, &lumirpc.CheckRequest{
Type: string(token),
Properties: props,
@ -121,6 +134,7 @@ func createResource(t *testing.T, res interface{}, provider lumirpc.ResourceProv
return "", nil
}
assert.Equal(t, 0, len(checkResp.Failures), "expected no check failures")
fmt.Printf("[Provider Test]: Creating %v\n", token)
resp, err := provider.Create(nil, &lumirpc.CreateRequest{
Type: string(token),
Properties: props,
@ -132,11 +146,23 @@ func createResource(t *testing.T, res interface{}, provider lumirpc.ResourceProv
return "", nil
}
id := resp.Id
fmt.Printf("[Provider Test]: Getting %v with id %v\n", token, id)
getResp, err := provider.Get(nil, &lumirpc.GetRequest{
Type: string(token),
Id: id,
})
if !assert.NoError(t, err, "expected no error reading resource") {
return "", nil
}
if !assert.NotNil(t, getResp, "expected a non-nil response reading the resources") {
return "", nil
}
return id, props
}
func updateResource(t *testing.T, id string, lastProps *structpb.Struct, res interface{}, provider lumirpc.ResourceProviderServer, token tokens.Type) (bool, *structpb.Struct) {
newProps := resource.MarshalProperties(nil, resource.NewPropertyMap(res), resource.MarshalOptions{})
fmt.Printf("[Provider Test]: Checking %v\n", token)
checkResp, err := provider.Check(nil, &lumirpc.CheckRequest{
Type: string(token),
Properties: newProps,
@ -145,6 +171,7 @@ func updateResource(t *testing.T, id string, lastProps *structpb.Struct, res int
return false, nil
}
assert.Equal(t, 0, len(checkResp.Failures), "expected no check failures")
fmt.Printf("[Provider Test]: Updating %v with id %v\n", token, id)
_, err = provider.Update(nil, &lumirpc.UpdateRequest{
Type: string(token),
Id: id,
@ -158,6 +185,7 @@ func updateResource(t *testing.T, id string, lastProps *structpb.Struct, res int
}
func deleteResource(t *testing.T, id string, provider lumirpc.ResourceProviderServer, token tokens.Type) bool {
fmt.Printf("[Provider Test]: Deleting %v with id %v\n", token, id)
_, err := provider.Delete(nil, &lumirpc.DeleteRequest{
Type: string(token),
Id: id,

View file

@ -49,14 +49,15 @@ func (p *AccountProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(AccountToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *AccountProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *APIKeyProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(APIKeyToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *APIKeyProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *AuthorizerProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(AuthorizerToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *AuthorizerProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *BasePathMappingProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(BasePathMappingToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *BasePathMappingProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *ClientCertificateProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(ClientCertificateToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *ClientCertificateProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *DeploymentProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(DeploymentToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *DeploymentProvider) Name(

View file

@ -97,14 +97,15 @@ func (p *MethodProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(MethodToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *MethodProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *ModelProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(ModelToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *ModelProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *ResourceProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(ResourceToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *ResourceProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *RestAPIProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(RestAPIToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *RestAPIProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *StageProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(StageToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *StageProvider) Name(

View file

@ -93,14 +93,15 @@ func (p *UsagePlanProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(UsagePlanToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *UsagePlanProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *UsagePlanKeyProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(UsagePlanKeyToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *UsagePlanKeyProvider) Name(

View file

@ -51,14 +51,15 @@ func (p *ActionTargetProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(ActionTargetToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *ActionTargetProvider) Name(
@ -222,14 +223,15 @@ func (p *AlarmProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(AlarmToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *AlarmProvider) Name(

View file

@ -87,14 +87,15 @@ func (p *TableProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(TableToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *TableProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *InstanceProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(InstanceToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *InstanceProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *InternetGatewayProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(InternetGatewayToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *InternetGatewayProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *RouteProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(RouteToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *RouteProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *RouteTableProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(RouteTableToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *RouteTableProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *SecurityGroupProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(SecurityGroupToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *SecurityGroupProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *SecurityGroupEgressProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(SecurityGroupEgressToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *SecurityGroupEgressProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *SecurityGroupIngressProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(SecurityGroupIngressToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *SecurityGroupIngressProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *SubnetProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(SubnetToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *SubnetProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *VPCProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(VPCToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *VPCProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *VPCGatewayAttachmentProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(VPCGatewayAttachmentToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *VPCGatewayAttachmentProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *VPCPeeringConnectionProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(VPCPeeringConnectionToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *VPCPeeringConnectionProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *ApplicationProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(ApplicationToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *ApplicationProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *ApplicationVersionProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(ApplicationVersionToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *ApplicationVersionProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *EnvironmentProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(EnvironmentToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *EnvironmentProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *GroupProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(GroupToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *GroupProvider) Name(

View file

@ -63,14 +63,15 @@ func (p *PolicyProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(PolicyToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *PolicyProvider) Name(

View file

@ -51,14 +51,15 @@ func (p *RoleProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(RoleToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *RoleProvider) Name(

View file

@ -63,14 +63,15 @@ func (p *UserProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(UserToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *UserProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *KeyProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(KeyToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *KeyProvider) Name(

View file

@ -63,14 +63,15 @@ func (p *FunctionProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(FunctionToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *FunctionProvider) Name(

View file

@ -51,14 +51,15 @@ func (p *PermissionProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(PermissionToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *PermissionProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *BucketProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(BucketToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *BucketProvider) Name(

View file

@ -48,14 +48,15 @@ func (p *ObjectProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(ObjectToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *ObjectProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *TopicProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(TopicToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *TopicProvider) Name(

View file

@ -49,14 +49,15 @@ func (p *QueueProvider) Check(
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
contract.Assert(req.GetType() == string(QueueToken))
obj, _, err := p.Unmarshal(req.GetProperties())
if err == nil {
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
err = resource.NewCheckError(failures)
}
if err != nil {
return resource.NewCheckResponse(err), nil
}
return resource.NewCheckResponse(err), nil
if failures, err := p.ops.Check(ctx, obj); err != nil {
return nil, err
} else if len(failures) > 0 {
return resource.NewCheckResponse(resource.NewCheckError(failures)), nil
}
return resource.NewCheckResponse(nil), nil
}
func (p *QueueProvider) Name(

View file

@ -1,3 +1,5 @@
name: lumijs
description: The LumiJS runtime library.
dependencies:
lumi: "*"

25
lib/lumijs/lib/console.ts Normal file
View file

@ -0,0 +1,25 @@
// Licensed to Pulumi Corporation ("Pulumi") under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// Pulumi licenses this file to You 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 { printf } from "@lumi/lumi/runtime"
export class Console {
log(message: any) {
printf(message);
printf("\n");
}
}
export let console = new Console();

View file

@ -17,4 +17,5 @@
export * from "./errors";
export * from "./types";
export * from "./console";

View file

@ -111,7 +111,6 @@ export class API {
let restAPI = new aws.apigateway.RestAPI(prefix, { body: body });
let deployment = new aws.apigateway.Deployment(prefix + "-deployment", {
restAPI: restAPI,
stageName: "Stage",
});
let stage = new aws.apigateway.Stage(prefix + "-primary-stage", {
deployment: deployment,

View file

@ -0,0 +1,107 @@
// Licensed to Pulumi Corporation ("Pulumi") under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// Pulumi licenses this file to You 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.
package binder
import (
"github.com/pulumi/lumi/pkg/compiler/ast"
"github.com/pulumi/lumi/pkg/tokens"
"github.com/pulumi/lumi/pkg/util/contract"
)
// FreeVars computes the free variables referenced inside a function body.
// The free variables for a function will be either simple identifier tokens or tokens
// referencing module-scope variables.
func FreeVars(fnc ast.Function) []tokens.Token {
visitor := &freeVarsVisitor{
freeVars: map[tokens.Token]bool{},
}
ast.Walk(visitor, fnc)
params := fnc.GetParameters()
if params != nil {
for _, lv := range *params {
visitor.removeLocalVariable(lv)
}
}
var vars []tokens.Token
for k := range visitor.freeVars {
vars = append(vars, k)
}
return vars
}
type freeVarsVisitor struct {
freeVars map[tokens.Token]bool
}
var _ ast.Visitor = (*freeVarsVisitor)(nil)
func (visitor *freeVarsVisitor) Visit(node ast.Node) ast.Visitor {
return visitor
}
// We walk the AST and process each node after visiting it in depth first order. There are two cases we care about:
// 1) After visiting a leaf node which is a reference to a local variable (`n.Object == nil``), we add it to our set.
// 2) After visiting a LocalVariableDeclaration or a Lambda, we remove the declared variables from our set.
func (visitor *freeVarsVisitor) After(node ast.Node) {
switch n := node.(type) {
case *ast.LoadLocationExpression:
if n.Object == nil {
visitor.addToken(n.Name.Tok)
}
case *ast.LoadDynamicExpression:
if n.Object == nil {
switch e := n.Name.(type) {
case *ast.StringLiteral:
visitor.addToken(tokens.Token(e.Value))
default:
contract.Failf("expected LoadDynamicExpression with Object == nil to have a StringLiteral expression")
}
}
case *ast.TryLoadDynamicExpression:
if n.Object == nil {
switch e := n.Name.(type) {
case *ast.StringLiteral:
visitor.addToken(tokens.Token(e.Value))
default:
contract.Failf("expected LoadDynamicExpression with Object == nil to have a StringLiteral expression")
}
}
case *ast.LambdaExpression:
if n.Parameters != nil {
for _, param := range *n.Parameters {
visitor.removeLocalVariable(param)
}
}
case *ast.Block:
for _, stmt := range n.Statements {
switch s := stmt.(type) {
case *ast.LocalVariableDeclaration:
visitor.removeLocalVariable(s.Local)
}
}
}
}
func (visitor *freeVarsVisitor) addToken(tok tokens.Token) {
visitor.freeVars[tok] = true
}
func (visitor *freeVarsVisitor) removeLocalVariable(lv *ast.LocalVariable) {
delete(visitor.freeVars, tokens.Token(lv.Name.Ident))
}

View file

@ -0,0 +1,192 @@
// Licensed to Pulumi Corporation ("Pulumi") under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// Pulumi licenses this file to You 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.
package binder
import (
"testing"
"github.com/pulumi/lumi/pkg/compiler/ast"
"github.com/pulumi/lumi/pkg/compiler/types"
"github.com/pulumi/lumi/pkg/tokens"
"github.com/stretchr/testify/assert"
)
func makeLocalVariable(name string) *ast.LocalVariable {
return &ast.LocalVariable{
DefinitionNode: ast.DefinitionNode{
Name: &ast.Identifier{
Ident: tokens.Name(name),
},
},
VariableNode: ast.VariableNode{
Type: &ast.TypeToken{
Tok: types.Object.TypeToken(),
},
},
}
}
func expressionRef(expr ast.Expression) *ast.Expression {
return &expr
}
func TestFreeVars_Parameter(t *testing.T) {
// function(foo) foo
fun := ast.ModuleMethod{
FunctionNode: ast.FunctionNode{
Parameters: &[]*ast.LocalVariable{
makeLocalVariable("foo"),
},
Body: &ast.ExpressionStatement{
Expression: &ast.LoadLocationExpression{
Name: &ast.Token{
Tok: tokens.Token("foo"),
},
},
},
},
}
freeVars := FreeVars(&fun)
assert.Len(t, freeVars, 0, "expected no free variables")
}
func TestFreeVars_Dynamic(t *testing.T) {
// function(foo) foo
fun := ast.ModuleMethod{
FunctionNode: ast.FunctionNode{
Parameters: &[]*ast.LocalVariable{},
Body: &ast.ExpressionStatement{
Expression: &ast.LoadDynamicExpression{
Name: &ast.StringLiteral{
Value: "foo",
},
},
},
},
}
freeVars := FreeVars(&fun)
assert.Len(t, freeVars, 1, "expected one free variable")
assert.Equal(t, tokens.Name("foo"), freeVars[0].Name())
}
func TestFreeVars_LocalVariable(t *testing.T) {
// function(foo) { var bar; foo; bar; baz; }
fun := ast.ModuleMethod{
FunctionNode: ast.FunctionNode{
Parameters: &[]*ast.LocalVariable{
makeLocalVariable("foo"),
},
Body: &ast.Block{
Statements: []ast.Statement{
&ast.LocalVariableDeclaration{
Local: makeLocalVariable("bar"),
},
&ast.ExpressionStatement{
Expression: &ast.LoadLocationExpression{
Name: &ast.Token{
Tok: tokens.Token("foo"),
},
},
},
&ast.ExpressionStatement{
Expression: &ast.LoadLocationExpression{
Name: &ast.Token{
Tok: tokens.Token("bar"),
},
},
},
&ast.ExpressionStatement{
Expression: &ast.TryLoadDynamicExpression{
Name: &ast.StringLiteral{
Value: "baz",
},
},
},
},
},
},
}
freeVars := FreeVars(&fun)
assert.Len(t, freeVars, 1, "expected one free variable")
assert.Equal(t, tokens.Name("baz"), freeVars[0].Name())
}
func TestFreeVars_Member(t *testing.T) {
// function(foo) foo.bar
fun := ast.ModuleMethod{
FunctionNode: ast.FunctionNode{
Parameters: &[]*ast.LocalVariable{
makeLocalVariable("foo"),
},
Body: &ast.ExpressionStatement{
Expression: &ast.LoadLocationExpression{
Object: expressionRef(&ast.LoadLocationExpression{
Name: &ast.Token{
Tok: tokens.Token("foo"),
},
}),
Name: &ast.Token{
Tok: tokens.Token("bar"),
},
},
},
},
}
freeVars := FreeVars(&fun)
assert.Len(t, freeVars, 0, "expected no free variables")
}
func TestFreeVars_Lambda(t *testing.T) {
// function(foo) ((bar) => bar)(foo)
fun := ast.ModuleMethod{
FunctionNode: ast.FunctionNode{
Parameters: &[]*ast.LocalVariable{
makeLocalVariable("foo"),
},
Body: &ast.ExpressionStatement{
Expression: &ast.InvokeFunctionExpression{
Function: &ast.LambdaExpression{
FunctionNode: ast.FunctionNode{
Parameters: &[]*ast.LocalVariable{
makeLocalVariable("bar"),
},
Body: &ast.ExpressionStatement{
Expression: &ast.LoadLocationExpression{
Name: &ast.Token{
Tok: tokens.Token("bar"),
},
},
},
},
},
CallExpressionNode: ast.CallExpressionNode{
Arguments: &[]*ast.CallArgument{
&ast.CallArgument{
Expr: &ast.LoadLocationExpression{
Name: &ast.Token{
Tok: tokens.Token("foo"),
},
},
},
},
},
},
},
},
}
freeVars := FreeVars(&fun)
assert.Len(t, freeVars, 0, "expected no free variables")
}

View file

@ -1730,22 +1730,23 @@ func (e *evaluator) evalLoadDynamicCore(node ast.Node, objexpr *ast.Expression,
}, nil
}
func (e *evaluator) getDynamicNameAddr(key tokens.Name, lval bool) *rt.Pointer {
var pv *rt.Pointer
// If there's no object, look in the current localsment.
pkey := rt.PropertyKey(key)
globals := e.getModuleGlobals(e.ctx.Currmodule)
if loc := e.locals.Lookup(key); loc != nil {
pv = e.locals.GetValueAddr(loc, true) // create a slot, we know the declaration exists.
} else {
// If it didn't exist in the lexical scope, check the module's globals.
pv = globals.Properties().GetAddr(pkey) // look for a global by this name, but don't allocate one.
func getDynamicNameAddrCore(locals rt.Environment, globals *rt.Object, key tokens.Name) *rt.Pointer {
if loc := locals.Lookup(key); loc != nil {
return locals.GetValueAddr(loc, true) // create a slot, we know the declaration exists.
}
// If it didn't exist in the lexical scope, check the module's globals.
pkey := rt.PropertyKey(key)
return globals.Properties().GetAddr(pkey) // look for a global by this name, but don't allocate one.
}
// Finally, if neither of those existed, and this is the target of a load, allocate a slot.
func (e *evaluator) getDynamicNameAddr(key tokens.Name, lval bool) *rt.Pointer {
globals := e.getModuleGlobals(e.ctx.Currmodule)
pv := getDynamicNameAddrCore(e.locals, globals, key)
// If not found and this is the target of a load, allocate a slot.
if pv == nil && lval {
if e.fnc != nil && e.fnc.SpecialModInit() && e.locals.Activation() {
pkey := rt.PropertyKey(key)
pv = globals.Properties().GetInitAddr(pkey)
} else {
loc := symbols.NewSpecialVariableSym(key, types.Dynamic)
@ -1870,7 +1871,8 @@ func (e *evaluator) evalLambdaExpression(node *ast.LambdaExpression) (*rt.Object
// To create a lambda object we will simply produce a function object that can invoke it. Lambdas also uniquely
// capture the current environment, including the this variable.
sig := e.ctx.RequireType(node).(*symbols.FunctionType)
obj := rt.NewFunctionObjectFromLambda(node, sig, e.locals)
moduleObject := e.getModuleGlobals(e.ctx.Currmodule)
obj := rt.NewFunctionObjectFromLambda(node, sig, e.locals, moduleObject)
return obj, nil
}

View file

@ -19,14 +19,15 @@ import (
"crypto/sha1"
"encoding/hex"
"fmt"
"sort"
"strconv"
"strings"
"github.com/pulumi/lumi/pkg/compiler/ast"
"github.com/pulumi/lumi/pkg/compiler/binder"
"github.com/pulumi/lumi/pkg/compiler/symbols"
"github.com/pulumi/lumi/pkg/compiler/types"
"github.com/pulumi/lumi/pkg/eval/rt"
"github.com/pulumi/lumi/pkg/tokens"
"github.com/pulumi/lumi/pkg/util/contract"
)
@ -108,21 +109,24 @@ func serializeClosure(intrin *rt.Intrinsic, e *evaluator, this *rt.Object, args
return e.NewException(intrin.Tree(), "Expected argument 'func' to be a lambda expression.")
}
// TODO[pulumi/lumi#177]: We are using the full environment available at execution time here, we should
// instead capture only the free variables referenced in the function itself.
// Insert environment variables into a PropertyMap with stable ordering
envPropMap := rt.NewPropertyMap()
slots := stub.Env.Slots()
var keys []*symbols.LocalVariable
for key := range slots {
keys = append(keys, key)
}
sort.SliceStable(keys, func(i, j int) bool {
return keys[i].Name() < keys[j].Name()
})
for _, key := range keys {
envPropMap.Set(rt.PropertyKey(key.Name()), slots[key].Obj())
for _, tok := range binder.FreeVars(stub.Func) {
var name tokens.Name
contract.Assertf(tok.Simple() || (tok.HasModule() && tok.HasModuleMember() && !tok.HasClassMember()),
"Expected free variable to be simple name or reference to top-level module name")
if tok.Simple() {
name = tok.Name()
} else {
name = tokens.Name(tok.ModuleMember().Name())
}
pv := getDynamicNameAddrCore(stub.Env, stub.Module, name)
if pv != nil {
envPropMap.Set(rt.PropertyKey(name), pv.Obj())
}
// Else the variable was not found, so we skip serializing it.
// This will be true for references to globals which are not known to Lumi but
// will be available within the runtime environment.
}
envObj := e.alloc.New(intrin.Tree(), types.Dynamic, envPropMap, nil)

View file

@ -404,7 +404,6 @@ func NewNullObject() *Object {
// NewStringObject creates a new primitive number object.
func NewStringObject(v string) *Object {
// Add a `length` property to the object
arrayProps := NewPropertyMap()
lengthGetter := NewBuiltinIntrinsic(
@ -413,9 +412,7 @@ func NewStringObject(v string) *Object {
)
arrayProps.InitAddr(PropertyKey("length"), nil, true, lengthGetter, nil)
stringProto := StringPrototypeObject()
return NewObject(types.String, v, arrayProps, stringProto)
return NewObject(types.String, v, arrayProps, StringPrototypeObject())
}
// stringProto is a cached reference to the String prototype object
@ -455,21 +452,23 @@ func NewFunctionObjectFromSymbol(fnc symbols.Function, this *Object) *Object {
}
// NewFunctionObjectFromLambda creates a new function object with very specific underlying parts.
func NewFunctionObjectFromLambda(fnc ast.Function, sig *symbols.FunctionType, env Environment) *Object {
func NewFunctionObjectFromLambda(fnc ast.Function, sig *symbols.FunctionType, env Environment, module *Object) *Object {
return NewFunctionObject(FuncStub{
Func: fnc,
Sig: sig,
Env: env,
Func: fnc,
Sig: sig,
Env: env,
Module: module,
})
}
// FuncStub is a stub that captures a symbol plus an optional instance 'this' object.
type FuncStub struct {
Func ast.Function // the function whose body AST to evaluate.
Sym symbols.Function // an optional function symbol that this AST belongs to.
Sig *symbols.FunctionType // the function type representing this function's signature.
This *Object // an optional "this" pointer to bind when invoking this function.
Env Environment // an optional environment to evaluate this function inside.
Func ast.Function // the function whose body AST to evaluate.
Sym symbols.Function // an optional function symbol that this AST belongs to.
Sig *symbols.FunctionType // the function type representing this function's signature.
This *Object // an optional "this" pointer to bind when invoking this function.
Env Environment // an optional environment to evaluate this function inside.
Module *Object // an optional module object to use for module variable lookups inside this function.
}
// NewPointerObject allocates a new pointer-like object that wraps the given reference.
@ -638,18 +637,33 @@ func (o *Object) PropertyValues() *PropertyMap {
func adjustPointerForThis(parent *Object, this *Object, prop *Pointer) *Pointer {
if parent != this {
if value := prop.Obj(); value != nil {
if _, isfunc := value.Type().(*symbols.FunctionType); isfunc {
contract.Assert(prop.Readonly()) // otherwise, writes to the resulting pointer could go missing.
switch vt := value.Type().(type) {
case *symbols.FunctionType:
// Function stubs must be adjusted so that `this` is correct.
stub := value.FunctionValue()
contract.Assert(stub.This == parent)
value = NewFunctionObject(FuncStub{
Func: stub.Func,
Sym: stub.Sym,
Sig: stub.Sig,
This: this,
Env: stub.Env,
Func: stub.Func,
Sym: stub.Sym,
Sig: stub.Sig,
This: this,
Env: stub.Env,
Module: stub.Module,
})
prop = NewPointer(value, true, prop.Getter(), prop.Setter())
prop = NewPointer(value, prop.Readonly(), prop.Getter(), prop.Setter())
case *symbols.ComputedType:
// Computed stubs must be adjusted so that any self-references are correct.
stub := value.ComputedValue()
var sources []*Object
for _, source := range stub.Sources {
if source == parent {
sources = append(sources, this)
} else {
sources = append(sources, source)
}
}
value = NewComputedObject(vt.Element, stub.Expr, sources)
prop = NewPointer(value, prop.Readonly(), prop.Getter(), prop.Setter())
}
}
}
@ -673,7 +687,7 @@ func (intrin *Intrinsic) SpecialModInit() bool { return false }
func (intrin *Intrinsic) Tree() diag.Diagable { return intrin.node }
func (intrin *Intrinsic) Function() ast.Function { return intrin.node }
func (intrin *Intrinsic) Signature() *symbols.FunctionType { return intrin.sig }
func (intrin *Intrinsic) String() string { return string(intrin.Name()) }
func (intrin *Intrinsic) String() string { return string(intrin.Token()) }
func (intrin *Intrinsic) UnderlyingSymbol() symbols.Function { return intrin.fnc }

View file

@ -42,16 +42,14 @@ const DefaultDeploymentReftag = "#ref"
// Deployment is a serializable vertex within a LumiGL graph, specifically for resource snapshots.
type Deployment struct {
ID *ID `json:"id,omitempty"` // the provider ID for this resource, if any.
Type tokens.Type `json:"type"` // this resource's full type token.
Inputs DeploymentProperties `json:"inputs,omitempty"` // the input properties from the program.
Outputs DeploymentProperties `json:"outputs,omitempty"` // the output properties from the resource provider.
ID *ID `json:"id,omitempty"` // the provider ID for this resource, if any.
Type tokens.Type `json:"type"` // this resource's full type token.
Inputs map[string]interface{} `json:"inputs,omitempty"` // the input properties from the program.
Outputs map[string]interface{} `json:"outputs,omitempty"` // the output properties from the resource provider.
}
// DeploymentProperties is a property map from resource key to the underlying property value.
type DeploymentProperties map[string]interface{}
func serializeDeploymentRecord(snap Snapshot, reftag string) *DeploymentRecord {
// SerializeDeploymentRecord serializes an entire snapshot using the given reftag for cross-resource references.
func SerializeDeploymentRecord(snap Snapshot, reftag string) *DeploymentRecord {
// Initialize the reftag if needed, and only serialize if overridden.
var refp *string
if reftag == "" {
@ -68,7 +66,7 @@ func serializeDeploymentRecord(snap Snapshot, reftag string) *DeploymentRecord {
m := res.URN()
contract.Assertf(string(m) != "", "Unexpected empty resource URN")
contract.Assertf(!resm.Has(m), "Unexpected duplicate resource URN '%v'", m)
resm.Add(m, serializeDeployment(res, reftag))
resm.Add(m, SerializeDeployment(res, reftag))
}
}
@ -87,8 +85,8 @@ func serializeDeploymentRecord(snap Snapshot, reftag string) *DeploymentRecord {
}
}
// serializeDeployment turns a resource into a LumiGL data structure suitable for serialization.
func serializeDeployment(res Resource, reftag string) *Deployment {
// SerializeDeployment turns a resource into a LumiGL data structure suitable for serialization.
func SerializeDeployment(res Resource, reftag string) *Deployment {
contract.Assert(res != nil)
// Only serialize the ID if it is non-empty.
@ -98,8 +96,14 @@ func serializeDeployment(res Resource, reftag string) *Deployment {
}
// Serialize all input and output properties recursively, and add them if non-empty.
inputs := serializeDeploymentProperties(res.Inputs(), reftag)
outputs := serializeDeploymentProperties(res.Outputs(), reftag)
var inputs map[string]interface{}
if inp := res.Inputs(); inp != nil {
inputs = SerializeDeploymentProperties(inp, reftag)
}
var outputs map[string]interface{}
if outp := res.Outputs(); outp != nil {
outputs = SerializeDeploymentProperties(outp, reftag)
}
return &Deployment{
ID: idp,
@ -109,22 +113,19 @@ func serializeDeployment(res Resource, reftag string) *Deployment {
}
}
// serializeDeploymentProperties serializes a resource property bag so that it's suitable for serialization.
func serializeDeploymentProperties(props PropertyMap, reftag string) DeploymentProperties {
var dst DeploymentProperties
// SerializeDeploymentProperties serializes a resource property bag so that it's suitable for serialization.
func SerializeDeploymentProperties(props PropertyMap, reftag string) map[string]interface{} {
dst := make(map[string]interface{})
for _, k := range StablePropertyKeys(props) {
if v := serializeDeploymentProperty(props[k], reftag); v != nil {
if dst == nil {
dst = make(DeploymentProperties)
}
if v := SerializeDeploymentProperty(props[k], reftag); v != nil {
dst[string(k)] = v
}
}
return dst
}
// serializeDeploymentProperty serializes a resource property value so that it's suitable for serialization.
func serializeDeploymentProperty(prop PropertyValue, reftag string) interface{} {
// SerializeDeploymentProperty serializes a resource property value so that it's suitable for serialization.
func SerializeDeploymentProperty(prop PropertyValue, reftag string) interface{} {
contract.Assert(!prop.IsComputed())
// Skip nulls and "outputs"; the former needn't be serialized, and the latter happens if there is an output
@ -135,21 +136,17 @@ func serializeDeploymentProperty(prop PropertyValue, reftag string) interface{}
// For arrays, make sure to recurse.
if prop.IsArray() {
var arr []interface{}
for _, elem := range prop.ArrayValue() {
if v := serializeDeploymentProperty(elem, reftag); v != nil {
arr = append(arr, v)
}
srcarr := prop.ArrayValue()
dstarr := make([]interface{}, len(srcarr))
for i, elem := range prop.ArrayValue() {
dstarr[i] = SerializeDeploymentProperty(elem, reftag)
}
if len(arr) > 0 {
return arr
}
return nil
return dstarr
}
// Also for objects, recurse and use naked properties.
if prop.IsObject() {
return serializeDeploymentProperties(prop.ObjectValue(), reftag)
return SerializeDeploymentProperties(prop.ObjectValue(), reftag)
}
// Morph resources into their equivalent `{ "#ref": "<URN>" }` form.
@ -163,15 +160,17 @@ func serializeDeploymentProperty(prop PropertyValue, reftag string) interface{}
return prop.V
}
func deserializeDeploymentProperties(props DeploymentProperties, reftag string) PropertyMap {
// DeserializeDeploymentProperties deserializes an entire map of deployment properties into a resource property map.
func DeserializeDeploymentProperties(props map[string]interface{}, reftag string) PropertyMap {
result := make(PropertyMap)
for k, prop := range props {
result[PropertyKey(k)] = deserializeDeploymentProperty(prop, reftag)
result[PropertyKey(k)] = DeserializeDeploymentProperty(prop, reftag)
}
return result
}
func deserializeDeploymentProperty(v interface{}, reftag string) PropertyValue {
// DeserializeDeploymentProperty deserializes a single deployment property into a resource property value.
func DeserializeDeploymentProperty(v interface{}, reftag string) PropertyValue {
if v != nil {
switch w := v.(type) {
case bool:
@ -183,7 +182,7 @@ func deserializeDeploymentProperty(v interface{}, reftag string) PropertyValue {
case []interface{}:
var arr []PropertyValue
for _, elem := range w {
arr = append(arr, deserializeDeploymentProperty(elem, reftag))
arr = append(arr, DeserializeDeploymentProperty(elem, reftag))
}
return NewArrayProperty(arr)
case map[string]interface{}:
@ -197,7 +196,7 @@ func deserializeDeploymentProperty(v interface{}, reftag string) PropertyValue {
}
// Otherwise, this is an arbitrary object value.
obj := deserializeDeploymentProperties(DeploymentProperties(w), reftag)
obj := DeserializeDeploymentProperties(w, reftag)
return NewObjectProperty(obj)
default:
contract.Failf("Unrecognized property type: %v", reflect.ValueOf(v))

View file

@ -0,0 +1,127 @@
// Licensed to Pulumi Corporation ("Pulumi") under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// Pulumi licenses this file to You 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.
package resource
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/pulumi/lumi/pkg/tokens"
)
// TestDeploymentSerialization creates a basic
func TestDeploymentSerialization(t *testing.T) {
res := NewResource(
ID("test-resource-x"),
NewURN(tokens.QName("test"), tokens.Module("resource/test"), tokens.Type("Test"), tokens.QName("resource-x")),
tokens.Type("Test"),
NewPropertyMapFromMap(map[string]interface{}{
"in-nil": nil,
"in-bool": true,
"in-float64": float64(1.5),
"in-string": "lumilumilo",
"in-array": []interface{}{"a", true, float64(32)},
"in-empty-array": []interface{}{},
"in-map": map[string]interface{}{
"a": true,
"b": float64(88),
"c": "c-see-saw",
"d": "d-dee-daw",
},
"in-empty-map": map[string]interface{}{},
}),
NewPropertyMapFromMap(map[string]interface{}{
"out-nil": nil,
"out-bool": false,
"out-float64": float64(76),
"out-string": "loyolumiloom",
"out-array": []interface{}{false, "zzxx"},
"out-empty-array": []interface{}{},
"out-map": map[string]interface{}{
"x": false,
"y": "z-zee-zaw",
"z": float64(999.9),
},
"out-empty-map": map[string]interface{}{},
}),
)
dep := SerializeDeployment(res, DefaultDeploymentReftag)
// assert some things about the deployment record:
assert.NotNil(t, dep)
assert.NotNil(t, dep.ID)
assert.Equal(t, ID("test-resource-x"), *dep.ID)
assert.Equal(t, tokens.Type("Test"), dep.Type)
// assert some things about the inputs:
assert.NotNil(t, dep.Inputs)
assert.Nil(t, dep.Inputs["in-nil"])
assert.NotNil(t, dep.Inputs["in-bool"])
assert.True(t, dep.Inputs["in-bool"].(bool))
assert.NotNil(t, dep.Inputs["in-float64"])
assert.Equal(t, float64(1.5), dep.Inputs["in-float64"].(float64))
assert.NotNil(t, dep.Inputs["in-string"])
assert.Equal(t, "lumilumilo", dep.Inputs["in-string"].(string))
assert.NotNil(t, dep.Inputs["in-array"])
assert.Equal(t, 3, len(dep.Inputs["in-array"].([]interface{})))
assert.Equal(t, "a", dep.Inputs["in-array"].([]interface{})[0])
assert.Equal(t, true, dep.Inputs["in-array"].([]interface{})[1])
assert.Equal(t, float64(32), dep.Inputs["in-array"].([]interface{})[2])
assert.NotNil(t, dep.Inputs["in-empty-array"])
assert.Equal(t, 0, len(dep.Inputs["in-empty-array"].([]interface{})))
assert.NotNil(t, dep.Inputs["in-map"])
inmap := dep.Inputs["in-map"].(map[string]interface{})
assert.Equal(t, 4, len(inmap))
assert.NotNil(t, inmap["a"])
assert.Equal(t, true, inmap["a"].(bool))
assert.NotNil(t, inmap["b"])
assert.Equal(t, float64(88), inmap["b"].(float64))
assert.NotNil(t, inmap["c"])
assert.Equal(t, "c-see-saw", inmap["c"].(string))
assert.NotNil(t, inmap["d"])
assert.Equal(t, "d-dee-daw", inmap["d"].(string))
assert.NotNil(t, dep.Inputs["in-empty-map"])
assert.Equal(t, 0, len(dep.Inputs["in-empty-map"].(map[string]interface{})))
// assert some things about the outputs:
assert.NotNil(t, dep.Outputs)
assert.Nil(t, dep.Outputs["out-nil"])
assert.NotNil(t, dep.Outputs["out-bool"])
assert.False(t, dep.Outputs["out-bool"].(bool))
assert.NotNil(t, dep.Outputs["out-float64"])
assert.Equal(t, float64(76), dep.Outputs["out-float64"].(float64))
assert.NotNil(t, dep.Outputs["out-string"])
assert.Equal(t, "loyolumiloom", dep.Outputs["out-string"].(string))
assert.NotNil(t, dep.Outputs["out-array"])
assert.Equal(t, 2, len(dep.Outputs["out-array"].([]interface{})))
assert.Equal(t, false, dep.Outputs["out-array"].([]interface{})[0])
assert.Equal(t, "zzxx", dep.Outputs["out-array"].([]interface{})[1])
assert.NotNil(t, dep.Outputs["out-empty-array"])
assert.Equal(t, 0, len(dep.Outputs["out-empty-array"].([]interface{})))
assert.NotNil(t, dep.Outputs["out-map"])
outmap := dep.Outputs["out-map"].(map[string]interface{})
assert.Equal(t, 3, len(outmap))
assert.NotNil(t, outmap["x"])
assert.Equal(t, false, outmap["x"].(bool))
assert.NotNil(t, outmap["y"])
assert.Equal(t, "z-zee-zaw", outmap["y"].(string))
assert.NotNil(t, outmap["z"])
assert.Equal(t, float64(999.9), outmap["z"].(float64))
assert.NotNil(t, dep.Outputs["out-empty-map"])
assert.Equal(t, 0, len(dep.Outputs["out-empty-map"].(map[string]interface{})))
}

View file

@ -41,7 +41,7 @@ func SerializeEnvfile(env *Env, snap Snapshot, reftag string) *Envfile {
// If snap is nil, that's okay, we will just create an empty deployment; otherwise, serialize the whole snapshot.
var latest *DeploymentRecord
if snap != nil {
latest = serializeDeploymentRecord(snap, reftag)
latest = SerializeDeploymentRecord(snap, reftag)
}
var config *ConfigMap
@ -78,8 +78,8 @@ func DeserializeEnvfile(ctx *Context, envfile *Envfile) (*Env, Snapshot) {
for _, kvp := range latest.Resources.Iter() {
// Deserialize the resource properties, if they exist.
res := kvp.Value
inputs := deserializeDeploymentProperties(res.Inputs, reftag)
outputs := deserializeDeploymentProperties(res.Outputs, reftag)
inputs := DeserializeDeploymentProperties(res.Inputs, reftag)
outputs := DeserializeDeploymentProperties(res.Outputs, reftag)
// And now just produce a resource object using the information available.
var id ID

View file

@ -176,11 +176,11 @@ func (p *plan) Provider(res Resource) (Provider, error) {
func (p *plan) Apply(prog Progress) (Snapshot, Step, State, error) {
// First go ahead and propagate IDs for unchanged resources.
for old, new := range p.unchanged {
contract.Assert(old.HasID())
contract.Assert(!new.HasID())
contract.Assert(HasID(old))
contract.Assert(!HasID(new))
id := old.ID()
new.SetID(id)
new.SetOutputsFrom(old)
CopyOutputs(old, new)
p.ctx.IDURN[id] = new.URN()
}
@ -284,7 +284,7 @@ func newPlan(ctx *Context, old Snapshot, new Snapshot, analyzers []tokens.QName)
for _, old := range pb.OldRes {
m := old.URN()
pb.Olds[m] = old
contract.Assert(old.HasID())
contract.Assert(HasID(old))
// Keep track of which dependents exist for all resources.
for dep := range old.Inputs().AllResources() {
pb.Depends[dep] = append(pb.Depends[dep], m)
@ -680,7 +680,7 @@ func (s *step) Apply() (State, error) {
// Invoke the Create RPC function for this provider:
contract.Assert(s.old == nil)
contract.Assert(s.new != nil)
contract.Assertf(!s.new.HasID(), "Resources being created must not have IDs already")
contract.Assertf(!HasID(s.new), "Resources being created must not have IDs already")
rst, err := prov.Create(s.new)
if err != nil {
return rst, err
@ -698,7 +698,7 @@ func (s *step) Apply() (State, error) {
// Invoke the Delete RPC function for this provider:
contract.Assert(s.old != nil)
contract.Assert(s.new == nil)
contract.Assertf(s.old.HasID(), "Resources being deleted must have IDs")
contract.Assertf(HasID(s.old), "Resources being deleted must have IDs")
if rst, err := prov.Delete(s.old); err != nil {
return rst, err
}
@ -708,7 +708,7 @@ func (s *step) Apply() (State, error) {
contract.Assert(s.old != nil)
contract.Assert(s.new != nil)
contract.Assert(s.old.Type() == s.new.Type())
contract.Assertf(s.old.HasID(), "Resources being updated must have IDs")
contract.Assertf(HasID(s.old), "Resources being updated must have IDs")
id := s.old.ID()
if rst, err := prov.Update(s.old, s.new); err != nil {
return rst, err
@ -724,7 +724,7 @@ func (s *step) Apply() (State, error) {
contract.Assert(s.old != nil)
contract.Assert(s.new != nil)
contract.Assert(s.old.Type() == s.new.Type())
contract.Assertf(s.old.HasID(), "Resources being replaced must have IDs")
contract.Assertf(HasID(s.old), "Resources being replaced must have IDs")
// There is nothing to do for OpReplace nodes; they are here to represent logical steps in the graph, and mostly
// for visualization purposes; there will be true OpReplaceCreate and OpReplaceDelete nodes in the graph.

View file

@ -57,21 +57,16 @@ type PropertyValue struct {
// Computed represents the absence of a property value, because it will be computed at some point in the future. It
// contains a property value which represents the underlying expected type of the eventual property value.
type Computed PropertyValue
// Eventual reflects the eventual type of property value that a computed property will contain.
func (v Computed) Eventual() PropertyValue {
return PropertyValue(v)
type Computed struct {
Sources []URN // the resources whose state contribute to this value.
Element PropertyValue // the eventual value (type) of the computed property.
}
// Output is a property value that will eventually be computed by the resource provider. If an output property is
// encountered, it means the resource has not yet been created, and so the output value is unavailable. Note that an
// output property is a special case of computed, but carries additional semantic meaning.
type Output PropertyValue
// Eventual reflects the eventual type of property value that an output property will contain.
func (v Output) Eventual() PropertyValue {
return PropertyValue(v)
type Output struct {
Element PropertyValue // the eventual value (type) of the output property.
}
type ReqError struct {
@ -453,8 +448,13 @@ func NewResourceProperty(v URN) PropertyValue { return PropertyValue{v}
func NewComputedProperty(v Computed) PropertyValue { return PropertyValue{v} }
func NewOutputProperty(v Output) PropertyValue { return PropertyValue{v} }
func MakeComputed(v PropertyValue) PropertyValue { return NewComputedProperty(Computed(v)) }
func MakeOutput(v PropertyValue) PropertyValue { return NewOutputProperty(Output(v)) }
func MakeComputed(v PropertyValue, sources []URN) PropertyValue {
return NewComputedProperty(Computed{Element: v, Sources: sources})
}
func MakeOutput(v PropertyValue) PropertyValue {
return NewOutputProperty(Output{Element: v})
}
// NewPropertyValue turns a value into a property value, provided it is of a legal "JSON-like" kind.
func NewPropertyValue(v interface{}) PropertyValue {
@ -610,10 +610,10 @@ func (v PropertyValue) CanBool() bool {
return true
}
if v.IsComputed() {
return v.ComputedValue().Eventual().CanBool()
return v.ComputedValue().Element.CanBool()
}
if v.IsOutput() {
return v.OutputValue().Eventual().CanBool()
return v.OutputValue().Element.CanBool()
}
return false
}
@ -624,10 +624,10 @@ func (v PropertyValue) CanNumber() bool {
return true
}
if v.IsComputed() {
return v.ComputedValue().Eventual().CanNumber()
return v.ComputedValue().Element.CanNumber()
}
if v.IsOutput() {
return v.OutputValue().Eventual().CanNumber()
return v.OutputValue().Element.CanNumber()
}
return false
}
@ -638,10 +638,10 @@ func (v PropertyValue) CanString() bool {
return true
}
if v.IsComputed() {
return v.ComputedValue().Eventual().CanString()
return v.ComputedValue().Element.CanString()
}
if v.IsOutput() {
return v.OutputValue().Eventual().CanString()
return v.OutputValue().Element.CanString()
}
return false
}
@ -652,10 +652,10 @@ func (v PropertyValue) CanArray() bool {
return true
}
if v.IsComputed() {
return v.ComputedValue().Eventual().CanArray()
return v.ComputedValue().Element.CanArray()
}
if v.IsOutput() {
return v.OutputValue().Eventual().CanArray()
return v.OutputValue().Element.CanArray()
}
return false
}
@ -666,10 +666,10 @@ func (v PropertyValue) CanObject() bool {
return true
}
if v.IsComputed() {
return v.ComputedValue().Eventual().CanObject()
return v.ComputedValue().Element.CanObject()
}
if v.IsOutput() {
return v.OutputValue().Eventual().CanObject()
return v.OutputValue().Element.CanObject()
}
return false
}
@ -680,10 +680,10 @@ func (v PropertyValue) CanResource() bool {
return true
}
if v.IsComputed() {
return v.ComputedValue().Eventual().CanResource()
return v.ComputedValue().Element.CanResource()
}
if v.IsOutput() {
return v.OutputValue().Eventual().CanResource()
return v.OutputValue().Element.CanResource()
}
return false
}
@ -710,9 +710,9 @@ func (v PropertyValue) TypeString() string {
} else if v.IsResource() {
return "resource"
} else if v.IsComputed() {
return "computed<" + v.ComputedValue().Eventual().TypeString() + ">"
return "computed<" + v.ComputedValue().Element.TypeString() + ">"
} else if v.IsOutput() {
return "output<" + v.OutputValue().Eventual().TypeString() + ">"
return "output<" + v.OutputValue().Element.TypeString() + ">"
}
contract.Failf("Unrecognized PropertyValue type")
return ""

View file

@ -179,6 +179,9 @@ func TestArrayPropertyValueDiffs(t *testing.T) {
assert.Equal(t, d5a1.ArrayValue()[i], update.Old)
assert.Equal(t, d5a2.ArrayValue()[i], update.New)
}
// from nil to empty array:
d6 := NewNullProperty().Diff(NewArrayProperty([]PropertyValue{}))
assert.NotNil(t, d6)
}
func TestObjectPropertyValueDiffs(t *testing.T) {

View file

@ -16,62 +16,18 @@
package resource
import (
"crypto/rand"
"encoding/hex"
"reflect"
"github.com/golang/glog"
"github.com/pulumi/lumi/pkg/compiler/symbols"
"github.com/pulumi/lumi/pkg/compiler/types"
"github.com/pulumi/lumi/pkg/compiler/types/predef"
"github.com/pulumi/lumi/pkg/eval/heapstate"
"github.com/pulumi/lumi/pkg/eval/rt"
"github.com/pulumi/lumi/pkg/tokens"
"github.com/pulumi/lumi/pkg/util/contract"
)
// ID is a unique resource identifier; it is managed by the provider and is mostly opaque to Lumi.
type ID string
func MaybeID(s *string) *ID {
var ret *ID
if s != nil {
id := ID(*s)
ret = &id
}
return ret
}
func (id ID) String() string { return string(id) }
func (id *ID) StringPtr() *string {
if id == nil {
return nil
}
ids := (*id).String()
return &ids
}
func IDStrings(ids []ID) []string {
ss := make([]string, len(ids))
for i, id := range ids {
ss[i] = id.String()
}
return ss
}
// Resource is an instance of a resource with an ID, type, and bag of state.
type Resource interface {
ID() ID // the resource's unique ID assigned by the provider (or blank if uncreated).
URN() URN // the resource's object urn, a human-friendly, unique name for the resource.
Type() tokens.Type // the resource's type.
Inputs() PropertyMap // the resource's input properties (as specified by the program).
Outputs() PropertyMap // the resource's output properties (as specified by the resource provider).
HasID() bool // returns true if the resource has been assigned an ID.
SetID(id ID) // assignes an ID to this resource, for those under creation.
HasURN() bool // returns true if the resource has been assigned URN.
SetURN(m URN) // assignes a URN to this resource, for those under creation.
SetOutputsFrom(src Resource) // copy all output properties from one resource to another.
ShallowClone() Resource // make a shallow clone of the resource.
ID() ID // the resource's unique ID assigned by the provider (or blank if uncreated).
SetID(id ID) // assignes an ID to this resource, for those under creation.
URN() URN // the resource's object urn, a human-friendly, unique name for the resource.
SetURN(m URN) // assignes a URN to this resource, for those under creation.
Type() tokens.Type // the resource's type.
Inputs() PropertyMap // the resource's input properties (as specified by the program).
Outputs() PropertyMap // the resource's output properties (as specified by the resource provider).
}
// State is returned when an error has occurred during a resource provider operation. It indicates whether the
@ -83,208 +39,29 @@ const (
StateUnknown
)
// IsResourceVertex returns true if the heap graph vertex has an object whose type is the standard resource class.
func IsResourceVertex(v *heapstate.ObjectVertex) bool {
return predef.IsResourceType(v.Obj().Type())
// HasID returns true if the given resource has been assigned an ID.
func HasID(r Resource) bool {
return r.ID() != ""
}
type resource struct {
id ID // the resource's unique ID, assigned by the resource provider (or blank if uncreated).
urn URN // the resource's object urn, a human-friendly, unique name for the resource.
t tokens.Type // the resource's type.
inputs PropertyMap // the resource's input properties (as specified by the program).
outputs PropertyMap // the resource's output properties (as specified by the resource provider).
// HasURN returns true if the given resource has been assigned a URN.
func HasURN(r Resource) bool {
return r.URN() != ""
}
func (r *resource) ID() ID { return r.id }
func (r *resource) URN() URN { return r.urn }
func (r *resource) Type() tokens.Type { return r.t }
func (r *resource) Inputs() PropertyMap { return r.inputs }
func (r *resource) Outputs() PropertyMap { return r.outputs }
func (r *resource) HasID() bool { return (string(r.id) != "") }
func (r *resource) SetID(id ID) {
contract.Requiref(!r.HasID(), "id", "empty")
glog.V(9).Infof("Assigning ID=%v to resource w/ URN=%v", id, r.urn)
r.id = id
}
func (r *resource) HasURN() bool { return (string(r.urn) != "") }
func (r *resource) SetURN(m URN) {
contract.Requiref(!r.HasURN(), "urn", "empty")
r.urn = m
}
// SetOutputsFrom copies all output properties from a src resource to the instance.
func (r *resource) SetOutputsFrom(src Resource) {
src.Outputs().ShallowCloneInto(r.Outputs())
// CopyOutputs copies all output properties from a src resource to the instance.
func CopyOutputs(src Resource, dst Resource) {
src.Outputs().ShallowCloneInto(dst.Outputs())
}
// ShallowClone clones a resource object so that any modifications to it are not reflected in the original. Note that
// the property map is only shallowly cloned so any mutations deep within it may get reflected in the original.
func (r *resource) ShallowClone() Resource {
func ShallowClone(r Resource) Resource {
return &resource{
id: r.id,
urn: r.urn,
t: r.t,
inputs: r.inputs.ShallowClone(),
outputs: r.outputs.ShallowClone(),
id: r.ID(),
urn: r.URN(),
t: r.Type(),
inputs: r.Inputs().ShallowClone(),
outputs: r.Outputs().ShallowClone(),
}
}
// NewResource creates a new resource from the information provided.
func NewResource(id ID, urn URN, t tokens.Type, inputs PropertyMap, outputs PropertyMap) Resource {
if inputs == nil {
inputs = make(PropertyMap)
}
if outputs == nil {
outputs = make(PropertyMap)
}
return &resource{
id: id,
urn: urn,
t: t,
inputs: inputs,
outputs: outputs,
}
}
// NewObjectResource creates a new resource object out of the runtime object provided. The context is used to resolve
// dependencies between resources and must contain all references that could be encountered.
func NewObjectResource(ctx *Context, obj *rt.Object) Resource {
t := obj.Type()
contract.Assert(predef.IsResourceType(t))
// Extract the urn. This must already exist.
urn, hasm := ctx.ObjURN[obj]
contract.Assertf(!hasm, "Object already assigned a urn '%v'; double allocation detected", urn)
// Do a deep copy of the resource properties. This ensures property serializability.
props := cloneObject(ctx, obj)
// Finally allocate and return the resource object; note that ID is left blank until the provider assignes one.
return NewResource("", urn, t.TypeToken(), props, nil)
}
// cloneObject creates a property map out of a runtime object. The result is fully serializable in the sense that it
// can be stored in a JSON or YAML file, serialized over an RPC interface, etc. In particular, any references to other
// resources are replaced with their urn equivalents, which the runtime understands.
func cloneObject(ctx *Context, obj *rt.Object) PropertyMap {
contract.Assert(obj != nil)
// Walk the object's properties and serialize them in a stable order.
src := obj.PropertyValues()
dest := make(PropertyMap)
for _, k := range src.Stable() {
obj := src.Get(k)
if v, ok := cloneObjectProperty(ctx, obj); ok {
dest[PropertyKey(k)] = v
}
}
return dest
}
// cloneObjectProperty creates a single property value out of a runtime object. It returns false if the property could
// not be stored in a property (e.g., it is a function or other unrecognized or unserializable runtime object).
func cloneObjectProperty(ctx *Context, obj *rt.Object) (PropertyValue, bool) {
t := obj.Type()
// Serialize resource references as URNs.
if predef.IsResourceType(t) {
// For resources, simply look up the urn from the resource map.
urn, hasm := ctx.ObjURN[obj]
contract.Assertf(hasm, "Missing object reference; possible out of order dependency walk")
return NewResourceProperty(urn), true
}
// Serialize simple primitive types with their primitive equivalents.
switch t {
case types.Null:
return NewNullProperty(), true
case types.Bool:
return NewBoolProperty(obj.BoolValue()), true
case types.Number:
return NewNumberProperty(obj.NumberValue()), true
case types.String:
return NewStringProperty(obj.StringValue()), true
case types.Object, types.Dynamic:
obj := cloneObject(ctx, obj) // an object literal, clone it
return NewObjectProperty(obj), true
}
// Serialize arrays, maps, and object instances in the obvious way.
// TODO: handle symbols.MapType.
switch t.(type) {
case *symbols.ArrayType:
var result []PropertyValue
for _, e := range *obj.ArrayValue() {
if v, ok := cloneObjectProperty(ctx, e.Obj()); ok {
result = append(result, v)
}
}
return NewArrayProperty(result), true
case *symbols.Class:
obj := cloneObject(ctx, obj) // a class, just deep clone it
return NewObjectProperty(obj), true
}
// If a computed value, we can propagate an unknown value, but only for certain cases.
if t.Computed() {
// If this is an output property, then this property will turn into an output. Otherwise, it will be marked
// completed. An output property is permitted in more places by virtue of the fact that it is expected not to
// exist during resource create operations, whereas all computed properties should have been resolved by then.
var makeProperty func(PropertyValue) PropertyValue
if !obj.ComputedValue().Expr {
makeProperty = MakeOutput
} else {
makeProperty = MakeComputed
}
// TODO[pulumi/lumi#90]: track the resource URNs that are implicated.
future := t.(*symbols.ComputedType).Element
switch future {
case types.Null:
return makeProperty(NewNullProperty()), true
case types.Bool:
return makeProperty(NewBoolProperty(false)), true
case types.Number:
return makeProperty(NewNumberProperty(0)), true
case types.String:
return makeProperty(NewStringProperty("")), true
case types.Object, types.Dynamic:
return makeProperty(NewObjectProperty(make(PropertyMap))), true
}
switch future.(type) {
case *symbols.ArrayType:
return makeProperty(NewArrayProperty(nil)), true
case *symbols.Class:
return makeProperty(NewObjectProperty(make(PropertyMap))), true
}
}
// We can safely skip serializing functions, however, anything else is unexpected at this point.
_, isfunc := t.(*symbols.FunctionType)
contract.Assertf(isfunc, "Unrecognized resource property object type '%v' (%v)", t, reflect.TypeOf(t))
return PropertyValue{}, false
}
// NewUniqueHex generates a new "random" hex string for use by resource providers. It has the given optional prefix and
// the total length is capped to the maxlen. Note that capping to maxlen necessarily increases the risk of collisions.
func NewUniqueHex(prefix string, randlen, maxlen int) string {
bs := make([]byte, randlen)
n, err := rand.Read(bs)
contract.Assert(err == nil)
contract.Assert(n == len(bs))
str := prefix + hex.EncodeToString(bs)
if len(str) > maxlen {
str = str[:maxlen]
}
return str
}
// NewUniqueHexID generates a new "random" hex ID for use by resource providers. It has the given optional prefix and
// the total length is capped to the maxlen. Note that capping to maxlen necessarily increases the risk of collisions.
func NewUniqueHexID(prefix string, randlen, maxlen int) ID {
return ID(NewUniqueHex(prefix, randlen, maxlen))
}

View file

@ -16,6 +16,8 @@
package resource
import (
"github.com/pulumi/lumi/pkg/compiler/types/predef"
"github.com/pulumi/lumi/pkg/eval/heapstate"
"github.com/pulumi/lumi/pkg/graph"
"github.com/pulumi/lumi/pkg/util/contract"
)
@ -127,3 +129,8 @@ func (e *resourceEdge) From() graph.Vertex {
return e.from
}
func (e *resourceEdge) FromObj() *resourceVertex { return e.from }
// IsResourceVertex returns true if the heap graph vertex has an object whose type is the standard resource class.
func IsResourceVertex(v *heapstate.ObjectVertex) bool {
return predef.IsResourceType(v.Obj().Type())
}

View file

@ -0,0 +1,80 @@
// Licensed to Pulumi Corporation ("Pulumi") under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// Pulumi licenses this file to You 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.
package resource
import (
"crypto/rand"
"encoding/hex"
"github.com/pulumi/lumi/pkg/util/contract"
)
// ID is a unique resource identifier; it is managed by the provider and is mostly opaque to Lumi.
type ID string
// String converts a resource ID into a string.
func (id ID) String() string {
return string(id)
}
// StringPtr converts an optional ID into an optional string.
func (id *ID) StringPtr() *string {
if id == nil {
return nil
}
ids := (*id).String()
return &ids
}
// IDStrings turns an array of resource IDs into an array of strings.
func IDStrings(ids []ID) []string {
ss := make([]string, len(ids))
for i, id := range ids {
ss[i] = id.String()
}
return ss
}
// MaybeID turns an optional string into an optional resource ID.
func MaybeID(s *string) *ID {
var ret *ID
if s != nil {
id := ID(*s)
ret = &id
}
return ret
}
// NewUniqueHex generates a new "random" hex string for use by resource providers. It has the given optional prefix and
// the total length is capped to the maxlen. Note that capping to maxlen necessarily increases the risk of collisions.
func NewUniqueHex(prefix string, randlen, maxlen int) string {
bs := make([]byte, randlen)
n, err := rand.Read(bs)
contract.Assert(err == nil)
contract.Assert(n == len(bs))
str := prefix + hex.EncodeToString(bs)
if len(str) > maxlen {
str = str[:maxlen]
}
return str
}
// NewUniqueHexID generates a new "random" hex ID for use by resource providers. It has the given optional prefix and
// the total length is capped to the maxlen. Note that capping to maxlen necessarily increases the risk of collisions.
func NewUniqueHexID(prefix string, randlen, maxlen int) ID {
return ID(NewUniqueHex(prefix, randlen, maxlen))
}

View file

@ -0,0 +1,198 @@
// Licensed to Pulumi Corporation ("Pulumi") under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// Pulumi licenses this file to You 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.
package resource
import (
"reflect"
"github.com/golang/glog"
"github.com/pulumi/lumi/pkg/compiler/symbols"
"github.com/pulumi/lumi/pkg/compiler/types"
"github.com/pulumi/lumi/pkg/compiler/types/predef"
"github.com/pulumi/lumi/pkg/eval/rt"
"github.com/pulumi/lumi/pkg/tokens"
"github.com/pulumi/lumi/pkg/util/contract"
)
type objectResource struct {
ctx *Context // the resource context this object is associated with.
id ID // the resource's unique ID, assigned by the resource provider (or blank if uncreated).
urn URN // the resource's object urn, a human-friendly, unique name for the resource.
obj *rt.Object // the resource's live object reference.
outputs PropertyMap // the resource's output properties (as specified by the resource provider).
}
// NewObjectResource creates a new resource object out of the runtime object provided. The context is used to resolve
// dependencies between resources and must contain all references that could be encountered.
func NewObjectResource(ctx *Context, obj *rt.Object) Resource {
contract.Assertf(predef.IsResourceType(obj.Type()), "Expected a resource type")
return &objectResource{
ctx: ctx,
obj: obj,
outputs: make(PropertyMap),
}
}
func (r *objectResource) ID() ID { return r.id }
func (r *objectResource) URN() URN { return r.urn }
func (r *objectResource) Type() tokens.Type { return r.obj.Type().TypeToken() }
func (r *objectResource) Inputs() PropertyMap { return cloneResource(r.ctx, r.obj) }
func (r *objectResource) Outputs() PropertyMap { return r.outputs }
func (r *objectResource) SetID(id ID) {
contract.Requiref(!HasID(r), "id", "empty")
glog.V(9).Infof("Assigning ID=%v to resource w/ URN=%v", id, r.urn)
r.id = id
}
func (r *objectResource) SetURN(m URN) {
contract.Requiref(!HasURN(r), "urn", "empty")
r.urn = m
}
// cloneResource creates a property map out of a resource's runtime object.
func cloneResource(ctx *Context, resobj *rt.Object) PropertyMap {
return cloneObject(ctx, resobj, resobj)
}
// cloneObject creates a property map out of a runtime object. The result is fully serializable in the sense that it
// can be stored in a JSON or YAML file, serialized over an RPC interface, etc. In particular, any references to other
// resources are replaced with their urn equivalents, which the runtime understands.
func cloneObject(ctx *Context, resobj *rt.Object, obj *rt.Object) PropertyMap {
contract.Assert(obj != nil)
props := obj.PropertyValues()
return cloneObjectProperties(ctx, resobj, props)
}
// cloneObjectProperty creates a single property value out of a runtime object. It returns false if the property could
// not be stored in a property (e.g., it is a function or other unrecognized or unserializable runtime object).
func cloneObjectProperty(ctx *Context, resobj *rt.Object, obj *rt.Object) (PropertyValue, bool) {
t := obj.Type()
// Serialize resource references as URNs.
if predef.IsResourceType(t) {
// For resources, simply look up the urn from the resource map.
urn, hasm := ctx.ObjURN[obj]
contract.Assertf(hasm, "Missing object reference for %v; possible out of order dependency walk", obj)
return NewResourceProperty(urn), true
}
// Serialize simple primitive types with their primitive equivalents.
switch t {
case types.Null:
return NewNullProperty(), true
case types.Bool:
return NewBoolProperty(obj.BoolValue()), true
case types.Number:
return NewNumberProperty(obj.NumberValue()), true
case types.String:
return NewStringProperty(obj.StringValue()), true
case types.Object, types.Dynamic:
result := cloneObject(ctx, resobj, obj) // an object literal, clone it
return NewObjectProperty(result), true
}
// Serialize arrays, maps, and object instances in the obvious way.
switch t.(type) {
case *symbols.ArrayType:
// Make a new array, clone each element, and return the result.
var result []PropertyValue
for _, e := range *obj.ArrayValue() {
if v, ok := cloneObjectProperty(ctx, obj, e.Obj()); ok {
result = append(result, v)
}
}
return NewArrayProperty(result), true
case *symbols.MapType:
// Make a new map, clone each property value, and return the result.
props := obj.PropertyValues()
result := cloneObjectProperties(ctx, resobj, props)
return NewObjectProperty(result), true
case *symbols.Class:
// Make a new object that contains a deep clone of the source.
result := cloneObject(ctx, resobj, obj)
return NewObjectProperty(result), true
}
// If a computed value, we can propagate an unknown value, but only for certain cases.
if t.Computed() {
// If this is an output property, then this property will turn into an output. Otherwise, it will be marked
// completed. An output property is permitted in more places by virtue of the fact that it is expected not to
// exist during resource create operations, whereas all computed properties should have been resolved by then.
comp := obj.ComputedValue()
outprop := (!comp.Expr && len(comp.Sources) == 1 && comp.Sources[0] == resobj)
var makeProperty func(PropertyValue) PropertyValue
if outprop {
// For output properties, we need not track any URNs.
makeProperty = func(v PropertyValue) PropertyValue {
return MakeOutput(v)
}
} else {
// For all other properties, we need to look up and store the URNs for all resource dependencies.
var urns []URN
for _, src := range comp.Sources {
// For all inter-resource references, materialize a URN. Skip this for intra-resource references!
if src != resobj {
urn, hasm := ctx.ObjURN[src]
contract.Assertf(hasm,
"Missing computed reference from %v to %v; possible out of order dependency walk", resobj, src)
urns = append(urns, urn)
}
}
makeProperty = func(v PropertyValue) PropertyValue {
return MakeComputed(v, urns)
}
}
future := t.(*symbols.ComputedType).Element
switch future {
case types.Null:
return makeProperty(NewNullProperty()), true
case types.Bool:
return makeProperty(NewBoolProperty(false)), true
case types.Number:
return makeProperty(NewNumberProperty(0)), true
case types.String:
return makeProperty(NewStringProperty("")), true
case types.Object, types.Dynamic:
return makeProperty(NewObjectProperty(make(PropertyMap))), true
}
switch future.(type) {
case *symbols.ArrayType:
return makeProperty(NewArrayProperty(nil)), true
case *symbols.Class:
return makeProperty(NewObjectProperty(make(PropertyMap))), true
}
}
// We can safely skip serializing functions, however, anything else is unexpected at this point.
_, isfunc := t.(*symbols.FunctionType)
contract.Assertf(isfunc, "Unrecognized resource property object type '%v' (%v)", t, reflect.TypeOf(t))
return PropertyValue{}, false
}
// cloneObjectProperties copies a resource's properties.
func cloneObjectProperties(ctx *Context, resobj *rt.Object, props *rt.PropertyMap) PropertyMap {
// Walk the object's properties and serialize them in a stable order.
result := make(PropertyMap)
for _, k := range props.Stable() {
if v, ok := cloneObjectProperty(ctx, resobj, props.Get(k)); ok {
result[PropertyKey(k)] = v
}
}
return result
}

View file

@ -0,0 +1,68 @@
// Licensed to Pulumi Corporation ("Pulumi") under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// Pulumi licenses this file to You 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.
package resource
import (
"github.com/golang/glog"
"github.com/pulumi/lumi/pkg/tokens"
"github.com/pulumi/lumi/pkg/util/contract"
)
// resource is a structure containing state associated with a resource. This resource may have been serialized and
// deserialized, or snapshotted from a live graph of resource objects. The value's state is not, however, associated
// with any runtime objects in memory that may be actively involved in ongoing computations.
type resource struct {
id ID // the resource's unique ID, assigned by the resource provider (or blank if uncreated).
urn URN // the resource's object urn, a human-friendly, unique name for the resource.
t tokens.Type // the resource's type.
inputs PropertyMap // the resource's input properties (as specified by the program).
outputs PropertyMap // the resource's output properties (as specified by the resource provider).
}
// NewResource creates a new resource value from existing resource state information.
func NewResource(id ID, urn URN, t tokens.Type, inputs PropertyMap, outputs PropertyMap) Resource {
if inputs == nil {
inputs = make(PropertyMap)
}
if outputs == nil {
outputs = make(PropertyMap)
}
return &resource{
id: id,
urn: urn,
t: t,
inputs: inputs,
outputs: outputs,
}
}
func (r *resource) ID() ID { return r.id }
func (r *resource) URN() URN { return r.urn }
func (r *resource) Type() tokens.Type { return r.t }
func (r *resource) Inputs() PropertyMap { return r.inputs }
func (r *resource) Outputs() PropertyMap { return r.outputs }
func (r *resource) SetID(id ID) {
contract.Requiref(!HasID(r), "id", "empty")
glog.V(9).Infof("Assigning ID=%v to resource w/ URN=%v", id, r.urn)
r.id = id
}
func (r *resource) SetURN(m URN) {
contract.Requiref(!HasURN(r), "urn", "empty")
r.urn = m
}

View file

@ -140,13 +140,13 @@ func MarshalPropertyValue(ctx *Context, v PropertyValue, opts MarshalOptions) (*
},
}, true
} else if v.IsComputed() {
e := v.ComputedValue().Eventual()
e := v.ComputedValue().Element
contract.Assert(!e.IsComputed())
w, known := MarshalPropertyValue(ctx, e, opts)
contract.Assert(known)
return w, false
} else if v.IsOutput() {
e := v.OutputValue().Eventual()
e := v.OutputValue().Element
contract.Assert(!e.IsComputed())
w, known := MarshalPropertyValue(ctx, e, opts)
contract.Assert(known)

View file

@ -54,7 +54,7 @@ func NewGraphSnapshot(ctx *Context, ns tokens.QName, pkg tokens.Package, args co
// If the old snapshot is non-nil, we need to register old IDs so they will be found below.
if old != nil {
for _, res := range old.Resources() {
contract.Assert(res.HasID())
contract.Assert(HasID(res))
ctx.URNOldIDs[res.URN()] = res.ID()
}
}
@ -97,6 +97,11 @@ func (s *snapshot) ResourceByObject(obj *rt.Object) Resource { return s.ctx.ObjR
func createResources(ctx *Context, ns tokens.QName, heap *heapstate.Heap, resobjs []*rt.Object) ([]Resource, error) {
var resources []Resource
for _, resobj := range resobjs {
// Make sure the URN doesn't already exist for this object.
if urn, has := ctx.ObjURN[resobj]; has {
contract.Failf("Object already assigned a URN '%v'; double allocation detected", urn)
}
// Create an object resource without a URN.
res := NewObjectResource(ctx, resobj)

View file

@ -270,14 +270,15 @@ func (g *RPCGenerator) EmitResource(w *bufio.Writer, module tokens.Module, pkg *
writefmtln(w, " ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {")
writefmtln(w, " contract.Assert(req.GetType() == string(%vToken))", name)
writefmtln(w, " obj, _, err := p.Unmarshal(req.GetProperties())")
writefmtln(w, " if err == nil {")
writefmtln(w, " if failures, err := p.ops.Check(ctx, obj); err != nil {")
writefmtln(w, " return nil, err")
writefmtln(w, " } else if len(failures) > 0 {")
writefmtln(w, " err = resource.NewCheckError(failures)")
writefmtln(w, " }")
writefmtln(w, " if err != nil {")
writefmtln(w, " return resource.NewCheckResponse(err), nil")
writefmtln(w, " }")
writefmtln(w, " return resource.NewCheckResponse(err), nil")
writefmtln(w, " if failures, err := p.ops.Check(ctx, obj); err != nil {")
writefmtln(w, " return nil, err")
writefmtln(w, " } else if len(failures) > 0 {")
writefmtln(w, " return resource.NewCheckResponse(resource.NewCheckError(failures)), nil")
writefmtln(w, " }")
writefmtln(w, " return resource.NewCheckResponse(nil), nil")
writefmtln(w, "}")
writefmtln(w, "")
writefmtln(w, "func (p *%vProvider) Name(", name)