From 05e1140469e38749a2777c173196f10170c4032c Mon Sep 17 00:00:00 2001 From: Doug-AWS Date: Thu, 9 Aug 2018 06:52:57 -0700 Subject: [PATCH 01/33] Updated blurb about installing via npm to note it installs the latest released version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 926340b160ff4..85d012a7f8427 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ on AWS. * For a detailed walkthrough, see [Getting Started] in the AWS CDK [User Guide] * See [Manual Installation](./MANUAL_INSTALLATION.md) for installing the CDK from a signed .zip file -Install the [AWS CDK Toolkit] from npm (requires [Node.js ≥ 8.11.x](https://nodejs.org/en/download)): +Install the latest released version of the [AWS CDK Toolkit] from npm (requires [Node.js ≥ 8.11.x](https://nodejs.org/en/download)): ```bash $ npm i -g aws-cdk From 6413a6ef058a48d51bda01fe7b75e482e610ab38 Mon Sep 17 00:00:00 2001 From: Doug Date: Thu, 9 Aug 2018 11:02:49 -0700 Subject: [PATCH 02/33] Expanded early info on using CLI to set creds/region (#537) --- docs/src/getting-started.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/src/getting-started.rst b/docs/src/getting-started.rst index bc5f8fb955539..759a883861346 100644 --- a/docs/src/getting-started.rst +++ b/docs/src/getting-started.rst @@ -26,7 +26,9 @@ Prerequisites command-line toolkit and language bindings. `AWS CLI `_ - recommended in general, and can be -used to setup the :ref:`credentials ` for your AWS account. +used to setup the credentials and region for your AWS account, +which must be specified to use the toolkit. +See :ref:`credentials ` for information on using the AWS CLI to set your credentials. Install the command-line toolkit -------------------------------- From 147ea2afc37fa8b56da1dfcd8f834018420f1702 Mon Sep 17 00:00:00 2001 From: Alex Casalboni Date: Mon, 13 Aug 2018 10:05:16 +0200 Subject: [PATCH 03/33] docs: fix minor typo (#551) --- docs/src/cloudformation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/cloudformation.rst b/docs/src/cloudformation.rst index 72f0b8e370914..e037640b6af05 100644 --- a/docs/src/cloudformation.rst +++ b/docs/src/cloudformation.rst @@ -28,7 +28,7 @@ other resources, depending on what bucket APIs are used). .. important:: - Generally, when building CDK apps, you shouldn't need to interact + Generally, when building CDK apps, you shouldn't need to interact with CloudFormation directly. However, there might be advanced use cases and migration scenarios where this might be required. We are also aware that there might be gaps in capabilities in the AWS Construct Library over time. From 488e65b6fd69237ae6306b59d3b6bc190ac0aa94 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Mon, 13 Aug 2018 12:25:00 +0300 Subject: [PATCH 04/33] feat(aws-lambda): multiple renames to utilize namespace semantics (#550) As we move to using namespace imports across the CDK, the naming we chose for Lambda classes become cumbersome (i.e. `lambda.LambdaRuntime`). This change renames all lambda classes to adhere to service semantics. BREAKING CHANGE * LambdaRef => lambda.FunctionRef * Lambda => lambda.Function * LambdaVersion => lambda.FunctionVersion * LambdaRuntime => lambda.Runtime * LambdaCode => lambda.Code (and all subclasses) * InlineJavaScriptLambda => lambda.InlineJavaScriptFunction (soon to be deprecated) * LambdaPermission => lambda.Permission * SingletonLambda => SingletonFunction Updated tests to use import-requires. Intentionally avoided file renames to facilitate code review. Will do so in a subsequent PR. Fixes #430 --- docs/src/aws-construct-lib.rst | 4 +- .../cdk-examples-typescript/chat-app/index.ts | 6 +- .../aws-cloudformation/lib/custom-resource.ts | 4 +- .../test/integ.trivial-lambda-resource.ts | 6 +- .../aws-cloudformation/test/test.resource.ts | 6 +- packages/@aws-cdk/aws-cloudwatch/README.md | 10 +- packages/@aws-cdk/aws-codebuild/README.md | 2 +- .../lib/pipeline-action.ts | 2 +- .../test/integ.pipeline.ts | 6 +- .../test/test.pipeline-action.ts | 6 +- packages/@aws-cdk/aws-lambda/README.md | 20 ++-- packages/@aws-cdk/aws-lambda/lib/alias.ts | 16 +-- packages/@aws-cdk/aws-lambda/lib/code.ts | 24 ++--- packages/@aws-cdk/aws-lambda/lib/inline.ts | 14 +-- .../@aws-cdk/aws-lambda/lib/lambda-ref.ts | 28 ++--- .../@aws-cdk/aws-lambda/lib/lambda-version.ts | 12 +-- packages/@aws-cdk/aws-lambda/lib/lambda.ts | 24 ++--- .../@aws-cdk/aws-lambda/lib/permission.ts | 2 +- packages/@aws-cdk/aws-lambda/lib/runtime.ts | 30 +++--- .../aws-lambda/lib/singleton-lambda.ts | 22 ++-- .../aws-lambda/test/integ.assets.file.ts | 8 +- .../aws-lambda/test/integ.assets.lit.ts | 8 +- .../@aws-cdk/aws-lambda/test/integ.inline.ts | 6 +- .../@aws-cdk/aws-lambda/test/integ.lambda.ts | 18 ++-- .../@aws-cdk/aws-lambda/test/test.alias.ts | 48 ++++----- .../@aws-cdk/aws-lambda/test/test.inline.ts | 4 +- .../@aws-cdk/aws-lambda/test/test.lambda.ts | 60 +++++------ .../aws-lambda/test/test.singleton-lambda.ts | 6 +- .../test/test.subscriptiondestination.ts | 10 +- packages/@aws-cdk/aws-logs/README.md | 4 +- packages/@aws-cdk/aws-sns/lib/topic-ref.ts | 2 +- .../@aws-cdk/aws-sns/test/integ.sns-lambda.ts | 2 +- packages/@aws-cdk/aws-sns/test/test.sns.ts | 4 +- .../runtime-values/test/integ.rtv.lambda.ts | 2 +- packages/aws-cdk-all/package.json | 100 ------------------ 35 files changed, 214 insertions(+), 312 deletions(-) delete mode 100644 packages/aws-cdk-all/package.json diff --git a/docs/src/aws-construct-lib.rst b/docs/src/aws-construct-lib.rst index f2e753a9ab223..212f6d6b2042a 100644 --- a/docs/src/aws-construct-lib.rst +++ b/docs/src/aws-construct-lib.rst @@ -31,7 +31,7 @@ Least-Privilege IAM policies IAM policies are automatically defined based on intent. For example, when subscribing an AWS SNS :py:class:`Topic <@aws-cdk/aws-sns.Topic>` to a AWS Lambda -:py:class:`Function <@aws-cdk/aws-lambda.Lambda>`, the function's IAM permission +:py:class:`Function <@aws-cdk/aws-lambda.Function>`, the function's IAM permission policy will automatically be modified to allow the specific topic to invoke the function. @@ -76,7 +76,7 @@ Many AWS resources emit AWS CloudWatch metrics as part of their normal operation be used to setup :py:mod:`Alarms <@aws-cdk/aws-cloudwatch.Alarm>` or included in :py:mod:`Dashboards <@aws-cdk/aws-cloudwatch.Dashboard>`. :py:mod:`Metric <@aws-cdk/aws-cloudwatch.Metric>` objects for AWS Constructs can be obtained -via ``metricXxx()`` methods. For example, the :py:meth:`metricDuration() <@aws-cdk/aws-lambda.LambdaRef.metricDuration>` +via ``metricXxx()`` methods. For example, the :py:meth:`metricDuration() <@aws-cdk/aws-lambda.FunctionRef.metricDuration>` method reports the execution time of an AWS Lambda function. For more information see the :doc:`refs/_aws-cdk_aws-cloudwatch` documentation. diff --git a/examples/cdk-examples-typescript/chat-app/index.ts b/examples/cdk-examples-typescript/chat-app/index.ts index 9c778fb2b6ea6..4bf4e6b768413 100644 --- a/examples/cdk-examples-typescript/chat-app/index.ts +++ b/examples/cdk-examples-typescript/chat-app/index.ts @@ -76,11 +76,11 @@ interface ChatAppFuncProps { /* * Extend Function as all of the Chat app functions have these common props. */ -class ChatAppFunction extends lambda.Lambda { +class ChatAppFunction extends lambda.Function { constructor(parent: cdk.Construct, name: string, props: ChatAppFuncProps) { super(parent, name, { - code: new lambda.LambdaS3Code(props.bucket, props.zipFile), - runtime: lambda.LambdaRuntime.NodeJS610, + code: new lambda.S3Code(props.bucket, props.zipFile), + runtime: lambda.Runtime.NodeJS610, handler: 'index.handler' }); } diff --git a/packages/@aws-cdk/aws-cloudformation/lib/custom-resource.ts b/packages/@aws-cdk/aws-cloudformation/lib/custom-resource.ts index f2785a82df30c..4083ba16615a8 100644 --- a/packages/@aws-cdk/aws-cloudformation/lib/custom-resource.ts +++ b/packages/@aws-cdk/aws-cloudformation/lib/custom-resource.ts @@ -15,11 +15,11 @@ export interface CustomResourceProps { /** * The Lambda provider that implements this custom resource. * - * We recommend using a SingletonLambda for this. + * We recommend using a lambda.SingletonFunction for this. * * Optional, exactly one of lamdaProvider or topicProvider must be set. */ - lambdaProvider?: lambda.LambdaRef; + lambdaProvider?: lambda.FunctionRef; /** * The SNS Topic for the provider that implements this custom resource. diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.trivial-lambda-resource.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.trivial-lambda-resource.ts index 12bb7663d26e8..0eab2e20186c2 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.trivial-lambda-resource.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.trivial-lambda-resource.ts @@ -22,13 +22,13 @@ class DemoResource extends cdk.Construct { super(parent, name); const resource = new cloudformation.CustomResource(this, 'Resource', { - lambdaProvider: new lambda.SingletonLambda(this, 'Singleton', { + lambdaProvider: new lambda.SingletonFunction(this, 'Singleton', { uuid: 'f7d4f730-4ee1-11e8-9c2d-fa7ae01bbebc', // This makes the demo only work as top-level TypeScript program, but that's fine for now - code: new lambda.LambdaInlineCode(fs.readFileSync('integ.trivial-lambda-provider.py', { encoding: 'utf-8' })), + code: new lambda.InlineCode(fs.readFileSync('integ.trivial-lambda-provider.py', { encoding: 'utf-8' })), handler: 'index.main', timeout: 300, - runtime: lambda.LambdaRuntime.Python27, + runtime: lambda.Runtime.Python27, }), properties: props }); diff --git a/packages/@aws-cdk/aws-cloudformation/test/test.resource.ts b/packages/@aws-cdk/aws-cloudformation/test/test.resource.ts index 24a10f34edd09..a79900bc11800 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/test.resource.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/test.resource.ts @@ -92,10 +92,10 @@ class TestCustomResource extends cdk.Construct { constructor(parent: cdk.Construct, name: string) { super(parent, name); - const singletonLambda = new lambda.SingletonLambda(this, 'Lambda', { + const singletonLambda = new lambda.SingletonFunction(this, 'Lambda', { uuid: 'TestCustomResourceProvider', - code: new lambda.LambdaInlineCode('def hello(): pass'), - runtime: lambda.LambdaRuntime.Python27, + code: new lambda.InlineCode('def hello(): pass'), + runtime: lambda.Runtime.Python27, handler: 'index.hello', timeout: 300, }); diff --git a/packages/@aws-cdk/aws-cloudwatch/README.md b/packages/@aws-cdk/aws-cloudwatch/README.md index abc1de4b205e0..467a4ccab0b12 100644 --- a/packages/@aws-cdk/aws-cloudwatch/README.md +++ b/packages/@aws-cdk/aws-cloudwatch/README.md @@ -8,11 +8,11 @@ attributes. Resources that expose metrics will have functions that look like `metricXxx()` which will return a Metric object, initialized with defaults that make sense. -For example, `Lambda` objects have the `lambda.metricErrors()` method, which +For example, `lambda.Function` objects have the `fn.metricErrors()` method, which represents the amount of errors reported by that Lambda function: ```ts -const errors = lambda.metricErrors(); +const errors = fn.metricErrors(); ``` ### Aggregation @@ -30,7 +30,7 @@ the function or the period), you can do so by passing additional parameters to the metric function call: ```ts -const minuteErrorRate = lambda.metricErrors({ +const minuteErrorRate = fn.metricErrors({ statistic: 'avg', periodSec: 60, label: 'Lambda failure rate' @@ -66,7 +66,7 @@ object, passing the `Metric` object to set the alarm on: ```ts new Alarm(this, 'Alarm', { - metric: lambda.metricErrors(), + metric: fn.metricErrors(), threshold: 100, evaluationPeriods: 2, }); @@ -75,7 +75,7 @@ new Alarm(this, 'Alarm', { Alternatively, you can call `metric.newAlarm()`: ```ts -lambda.metricErrors().newAlarm(this, 'Alarm', { +fn.metricErrors().newAlarm(this, 'Alarm', { threshold: 100, evaluationPeriods: 2, }); diff --git a/packages/@aws-cdk/aws-codebuild/README.md b/packages/@aws-cdk/aws-codebuild/README.md index cad980e5cd09d..9b48621af3d7a 100644 --- a/packages/@aws-cdk/aws-codebuild/README.md +++ b/packages/@aws-cdk/aws-codebuild/README.md @@ -52,5 +52,5 @@ To define CloudWatch event rules for build projects, use one of the `onXxx` meth ```ts const rule = project.onStateChange('BuildStateChange'); -rule.addTarget(lambda); +rule.addTarget(lambdaFunction); ``` diff --git a/packages/@aws-cdk/aws-lambda-codepipeline/lib/pipeline-action.ts b/packages/@aws-cdk/aws-lambda-codepipeline/lib/pipeline-action.ts index e64f8477ce32f..a2088ecf6c308 100644 --- a/packages/@aws-cdk/aws-lambda-codepipeline/lib/pipeline-action.ts +++ b/packages/@aws-cdk/aws-lambda-codepipeline/lib/pipeline-action.ts @@ -9,7 +9,7 @@ export interface PipelineInvokeActionProps { /** * The lambda function to invoke. */ - lambda: lambda.LambdaRef; + lambda: lambda.FunctionRef; /** * String to be used in the event data parameter passed to the Lambda diff --git a/packages/@aws-cdk/aws-lambda-codepipeline/test/integ.pipeline.ts b/packages/@aws-cdk/aws-lambda-codepipeline/test/integ.pipeline.ts index fd132d3b1dfe1..bdc7199b033f2 100644 --- a/packages/@aws-cdk/aws-lambda-codepipeline/test/integ.pipeline.ts +++ b/packages/@aws-cdk/aws-lambda-codepipeline/test/integ.pipeline.ts @@ -20,14 +20,14 @@ new codepipeline.AmazonS3Source(sourceStage, 'Source', { bucketKey: 'key', }); -const lambdaFun = new lambda.Lambda(stack, 'LambdaFun', { - code: new lambda.LambdaInlineCode(` +const lambdaFun = new lambda.Function(stack, 'LambdaFun', { + code: new lambda.InlineCode(` exports.handler = function () { console.log("Hello, world!"); }; `), handler: 'index.handler', - runtime: lambda.LambdaRuntime.NodeJS610, + runtime: lambda.Runtime.NodeJS610, }); const lambdaStage = new codepipeline.Stage(pipeline, 'Lambda'); new lambda_codepipeline.PipelineInvokeAction(lambdaStage, 'Lambda', { diff --git a/packages/@aws-cdk/aws-lambda-codepipeline/test/test.pipeline-action.ts b/packages/@aws-cdk/aws-lambda-codepipeline/test/test.pipeline-action.ts index e2c5ed26a3e2a..b9513222f8827 100644 --- a/packages/@aws-cdk/aws-lambda-codepipeline/test/test.pipeline-action.ts +++ b/packages/@aws-cdk/aws-lambda-codepipeline/test/test.pipeline-action.ts @@ -11,10 +11,10 @@ export = { 'PipelineInvokeAction can be used to invoke lambda functions from a CodePipeline'(test: Test) { const stack = new cdk.Stack(); - const lambdaFun = new lambda.Lambda(stack, 'Function', { - code: new lambda.LambdaInlineCode('bla'), + const lambdaFun = new lambda.Function(stack, 'Function', { + code: new lambda.InlineCode('bla'), handler: 'index.handler', - runtime: lambda.LambdaRuntime.NodeJS43, + runtime: lambda.Runtime.NodeJS43, }); const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index 2ee137544755f..78d22f32f2cc5 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -1,28 +1,30 @@ ## AWS Lambda Construct Library -This construct library allows you to define AWS Lambda functions. +This construct library allows you to define AWS Lambda Functions. ```ts -const fn = new Lambda(this, 'MyFunction', { - runtime: LambdaRuntime.NodeJS810, +import lambda = require('@aws-cdk/aws-lambda'); + +const fn = new lambda.Function(this, 'MyFunction', { + runtime: lambda.Runtime.NodeJS810, handler: 'index.handler' - code: LambdaCode.inline('exports.handler = function(event, ctx, cb) { return cb(null, "hi"); }'), + code: lambda.Code.inline('exports.handler = function(event, ctx, cb) { return cb(null, "hi"); }'), }); ``` ### Handler Code -The `LambdaCode` class includes static convenience methods for various types of +The `lambda.Code` class includes static convenience methods for various types of runtime code. - * `LambdaCode.bucket(bucket, key[, objectVersion])` - specify an S3 object that + * `lambda.Code.bucket(bucket, key[, objectVersion])` - specify an S3 object that contains the archive of your runtime code. - * `LambdaCode.inline(code)` - inline the handle code as a string. This is + * `lambda.Code.inline(code)` - inline the handle code as a string. This is limited to 4KB. The class `InlineJavaScriptLambda` can be used to simplify inlining JavaScript functions. - * `LambdaCode.directory(directory)` - specify a directory in the local filesystem + * `lambda.Code.directory(directory)` - specify a directory in the local filesystem which will be zipped and uploaded to S3 before deployment. - * `LambdaCode.file(path)` - specify a file to be used for Lambda code. This can + * `lambda.Code.file(path)` - specify a file to be used for Lambda code. This can be, for example a JAR or a ZIP file, based on the runtime used. The following example shows how to define a Python function and deploy the code from the diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index ac7c143ec9755..11b07bcb65fc5 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -1,9 +1,9 @@ import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); -import { FunctionName, LambdaRef } from './lambda-ref'; -import { LambdaVersion } from './lambda-version'; +import { FunctionName, FunctionRef } from './lambda-ref'; +import { FunctionVersion } from './lambda-version'; import { cloudformation, FunctionArn } from './lambda.generated'; -import { LambdaPermission } from './permission'; +import { Permission } from './permission'; /** * Properties for a new Lambda alias @@ -21,7 +21,7 @@ export interface AliasProps { * * Use lambda.addVersion() to obtain a new lambda version to refer to. */ - version: LambdaVersion; + version: FunctionVersion; /** * Name of this alias @@ -51,7 +51,7 @@ export interface AliasProps { /** * A new alias to a particular version of a Lambda function. */ -export class Alias extends LambdaRef { +export class Alias extends FunctionRef { /** * ARN of this alias * @@ -78,7 +78,7 @@ export class Alias extends LambdaRef { /** * The actual Lambda function object that this Alias is pointing to */ - private readonly underlyingLambda: LambdaRef; + private readonly underlyingLambda: FunctionRef; constructor(parent: cdk.Construct, name: string, props: AliasProps) { super(parent, name); @@ -97,7 +97,7 @@ export class Alias extends LambdaRef { this.functionArn = alias.ref; } - public addPermission(name: string, permission: LambdaPermission) { + public addPermission(name: string, permission: Permission) { // Forward addPermission() to the underlying Lambda object this.underlyingLambda.addPermission(name, permission); } @@ -146,7 +146,7 @@ export interface VersionWeight { /** * The version to route traffic to */ - readonly version: LambdaVersion; + readonly version: FunctionVersion; /** * How much weight to assign to this version (0..1) diff --git a/packages/@aws-cdk/aws-lambda/lib/code.ts b/packages/@aws-cdk/aws-lambda/lib/code.ts index 4a450f926ac69..4eb410263c6f7 100644 --- a/packages/@aws-cdk/aws-lambda/lib/code.ts +++ b/packages/@aws-cdk/aws-lambda/lib/code.ts @@ -1,9 +1,9 @@ import assets = require('@aws-cdk/assets'); import s3 = require('@aws-cdk/aws-s3'); -import { Lambda } from './lambda'; +import { Function as Func } from './lambda'; import { cloudformation } from './lambda.generated'; -export abstract class LambdaCode { +export abstract class Code { /** * @returns `LambdaS3Code` associated with the specified S3 object. * @param bucket The S3 bucket @@ -11,7 +11,7 @@ export abstract class LambdaCode { * @param objectVersion Optional S3 object version */ public static bucket(bucket: s3.BucketRef, key: string, objectVersion?: string) { - return new LambdaS3Code(bucket, key, objectVersion); + return new S3Code(bucket, key, objectVersion); } /** @@ -19,7 +19,7 @@ export abstract class LambdaCode { * @param code The actual handler code (limited to 4KiB) */ public static inline(code: string) { - return new LambdaInlineCode(code); + return new InlineCode(code); } /** @@ -28,7 +28,7 @@ export abstract class LambdaCode { * @param directoryToZip The directory to zip */ public static directory(directoryToZip: string) { - return new LambdaAssetCode(directoryToZip, assets.AssetPackaging.ZipDirectory); + return new AssetCode(directoryToZip, assets.AssetPackaging.ZipDirectory); } /** @@ -36,7 +36,7 @@ export abstract class LambdaCode { * @param filePath The file path */ public static file(filePath: string) { - return new LambdaAssetCode(filePath, assets.AssetPackaging.File); + return new AssetCode(filePath, assets.AssetPackaging.File); } /** @@ -49,7 +49,7 @@ export abstract class LambdaCode { * Called when the lambda is initialized to allow this object to * bind to the stack, add resources and have fun. */ - public bind(_lambda: Lambda) { + public bind(_lambda: Func) { return; } } @@ -57,7 +57,7 @@ export abstract class LambdaCode { /** * Lambda code from an S3 archive. */ -export class LambdaS3Code extends LambdaCode { +export class S3Code extends Code { private bucketName: s3.BucketName; constructor(bucket: s3.BucketRef, private key: string, private objectVersion?: string) { @@ -82,7 +82,7 @@ export class LambdaS3Code extends LambdaCode { /** * Lambda code from an inline string (limited to 4KiB). */ -export class LambdaInlineCode extends LambdaCode { +export class InlineCode extends Code { constructor(private code: string) { super(); @@ -91,7 +91,7 @@ export class LambdaInlineCode extends LambdaCode { } } - public bind(lambda: Lambda) { + public bind(lambda: Func) { if (!lambda.runtime.supportsInlineCode) { throw new Error(`Inline source not allowed for ${lambda.runtime.name}`); } @@ -107,7 +107,7 @@ export class LambdaInlineCode extends LambdaCode { /** * Lambda code from a local directory. */ -export class LambdaAssetCode extends LambdaCode { +export class AssetCode extends Code { private asset?: assets.Asset; /** @@ -120,7 +120,7 @@ export class LambdaAssetCode extends LambdaCode { super(); } - public bind(lambda: Lambda) { + public bind(lambda: Func) { this.asset = new assets.Asset(lambda, 'Code', { path: this.path, packaging: this.packaging diff --git a/packages/@aws-cdk/aws-lambda/lib/inline.ts b/packages/@aws-cdk/aws-lambda/lib/inline.ts index 2c841c6cc6660..f58b30c313c13 100644 --- a/packages/@aws-cdk/aws-lambda/lib/inline.ts +++ b/packages/@aws-cdk/aws-lambda/lib/inline.ts @@ -1,7 +1,7 @@ import { Construct } from '@aws-cdk/cdk'; -import { LambdaInlineCode } from './code'; -import { Lambda } from './lambda'; -import { InlinableJavascriptLambdaRuntime, LambdaRuntime } from './runtime'; +import { InlineCode } from './code'; +import { Function } from './lambda'; +import { InlinableJavaScriptRuntime, Runtime } from './runtime'; /** * Defines the handler code for an inline JavaScript lambda function. @@ -68,7 +68,7 @@ export interface InlineJavaScriptLambdaProps { * * @default NodeJS810 */ - runtime?: InlinableJavascriptLambdaRuntime; + runtime?: InlinableJavaScriptRuntime; /** * A name for the function. If you don't specify a name, AWS CloudFormation @@ -105,10 +105,10 @@ export interface InlineJavaScriptLambdaProps { * This will define a node.js 6.10 function with the provided function has * the handler code. */ -export class InlineJavaScriptLambda extends Lambda { +export class InlineJavaScriptFunction extends Function { constructor(parent: Construct, name: string, props: InlineJavaScriptLambdaProps) { - const code = new LambdaInlineCode(renderCode(props.handler)); - const runtime: InlinableJavascriptLambdaRuntime = props.runtime || LambdaRuntime.NodeJS610; + const code = new InlineCode(renderCode(props.handler)); + const runtime: InlinableJavaScriptRuntime = props.runtime || Runtime.NodeJS610; const handler = 'index.handler'; const timeout = props.timeout || 30; super(parent, name, { diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts b/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts index 8eafd2b30d4da..79402f1a4ff6f 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts @@ -4,12 +4,12 @@ import iam = require('@aws-cdk/aws-iam'); import logs = require('@aws-cdk/aws-logs'); import cdk = require('@aws-cdk/cdk'); import { cloudformation, FunctionArn } from './lambda.generated'; -import { LambdaPermission } from './permission'; +import { Permission } from './permission'; /** * Represents a Lambda function defined outside of this stack. */ -export interface LambdaRefProps { +export interface FunctionRefProps { /** * The ARN of the Lambda function. * Format: arn::lambda:::function: @@ -23,7 +23,7 @@ export interface LambdaRefProps { role?: iam.Role; } -export abstract class LambdaRef extends cdk.Construct implements events.IEventRuleTarget, logs.ILogSubscriptionDestination { +export abstract class FunctionRef extends cdk.Construct implements events.IEventRuleTarget, logs.ILogSubscriptionDestination { /** * Creates a Lambda function object which represents a function not defined * within this stack. @@ -35,7 +35,7 @@ export abstract class LambdaRef extends cdk.Construct implements events.IEventRu * @param ref A reference to a Lambda function. Can be created manually (see * example above) or obtained through a call to `lambda.export()`. */ - public static import(parent: cdk.Construct, name: string, ref: LambdaRefProps): LambdaRef { + public static import(parent: cdk.Construct, name: string, ref: FunctionRefProps): FunctionRef { return new LambdaRefImport(parent, name, ref); } @@ -55,7 +55,7 @@ export abstract class LambdaRef extends cdk.Construct implements events.IEventRu * @default sum over 5 minutes */ public static metricAllErrors(props?: cloudwatch.MetricCustomization): cloudwatch.Metric { - return LambdaRef.metricAll('Errors', { statistic: 'sum', ...props }); + return FunctionRef.metricAll('Errors', { statistic: 'sum', ...props }); } /** @@ -64,7 +64,7 @@ export abstract class LambdaRef extends cdk.Construct implements events.IEventRu * @default average over 5 minutes */ public static metricAllDuration(props?: cloudwatch.MetricCustomization): cloudwatch.Metric { - return LambdaRef.metricAll('Duration', props); + return FunctionRef.metricAll('Duration', props); } /** @@ -73,7 +73,7 @@ export abstract class LambdaRef extends cdk.Construct implements events.IEventRu * @default sum over 5 minutes */ public static metricAllInvocations(props?: cloudwatch.MetricCustomization): cloudwatch.Metric { - return LambdaRef.metricAll('Invocations', { statistic: 'sum', ...props }); + return FunctionRef.metricAll('Invocations', { statistic: 'sum', ...props }); } /** @@ -82,7 +82,7 @@ export abstract class LambdaRef extends cdk.Construct implements events.IEventRu * @default sum over 5 minutes */ public static metricAllThrottles(props?: cloudwatch.MetricCustomization): cloudwatch.Metric { - return LambdaRef.metricAll('Throttles', { statistic: 'sum', ...props }); + return FunctionRef.metricAll('Throttles', { statistic: 'sum', ...props }); } /** @@ -97,7 +97,7 @@ export abstract class LambdaRef extends cdk.Construct implements events.IEventRu // probably what you're interested in if you're looking at this metric // (Load spikes may lead to concurrent execution errors that would // otherwise not be visible in the avg) - return LambdaRef.metricAll('ConcurrentExecutions', { statistic: 'max', ...props }); + return FunctionRef.metricAll('ConcurrentExecutions', { statistic: 'max', ...props }); } /** @@ -106,7 +106,7 @@ export abstract class LambdaRef extends cdk.Construct implements events.IEventRu * @default max over 5 minutes */ public static metricAllUnreservedConcurrentExecutions(props?: cloudwatch.MetricCustomization): cloudwatch.Metric { - return LambdaRef.metricAll('UnreservedConcurrentExecutions', { statistic: 'max', ...props }); + return FunctionRef.metricAll('UnreservedConcurrentExecutions', { statistic: 'max', ...props }); } /** @@ -146,7 +146,7 @@ export abstract class LambdaRef extends cdk.Construct implements events.IEventRu * Adds a permission to the Lambda resource policy. * @param name A name for the permission construct */ - public addPermission(name: string, permission: LambdaPermission) { + public addPermission(name: string, permission: Permission) { if (!this.canCreatePermissions) { // FIXME: Report metadata return; @@ -261,7 +261,7 @@ export abstract class LambdaRef extends cdk.Construct implements events.IEventRu /** * Export this Function (without the role) */ - public export(): LambdaRefProps { + public export(): FunctionRefProps { return { functionArn: new cdk.Output(this, 'FunctionArn', { value: this.functionArn }).makeImportValue(), }; @@ -287,14 +287,14 @@ export abstract class LambdaRef extends cdk.Construct implements events.IEventRu } } -class LambdaRefImport extends LambdaRef { +class LambdaRefImport extends FunctionRef { public readonly functionName: FunctionName; public readonly functionArn: FunctionArn; public readonly role?: iam.Role; protected readonly canCreatePermissions = false; - constructor(parent: cdk.Construct, name: string, props: LambdaRefProps) { + constructor(parent: cdk.Construct, name: string, props: FunctionRefProps) { super(parent, name); this.functionArn = props.functionArn; diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts index 04507ca33da28..fc1f82c0bb1b8 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts @@ -1,11 +1,11 @@ import { Construct } from '@aws-cdk/cdk'; -import { LambdaRef } from './lambda-ref'; +import { FunctionRef } from './lambda-ref'; import { cloudformation, Version } from './lambda.generated'; /** * Properties for a new Lambda version */ -export interface LambdaVersionProps { +export interface FunctionVersionProps { /** * SHA256 of the version of the Lambda source code * @@ -25,7 +25,7 @@ export interface LambdaVersionProps { /** * Function to get the value of */ - lambda: LambdaRef; + lambda: FunctionRef; } /** @@ -44,7 +44,7 @@ export interface LambdaVersionProps { * the right deployment, specify the `codeSha256` property while * creating the `Version. */ -export class LambdaVersion extends Construct { +export class FunctionVersion extends Construct { /** * The most recently deployed version of this function. */ @@ -53,9 +53,9 @@ export class LambdaVersion extends Construct { /** * Lambda object this version is associated with */ - public readonly lambda: LambdaRef; + public readonly lambda: FunctionRef; - constructor(parent: Construct, name: string, props: LambdaVersionProps) { + constructor(parent: Construct, name: string, props: FunctionVersionProps) { super(parent, name); const version = new cloudformation.VersionResource(this, 'Resource', { diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda.ts b/packages/@aws-cdk/aws-lambda/lib/lambda.ts index c1f389bdda2b4..620ffe1ceff55 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda.ts @@ -1,18 +1,18 @@ import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); -import { LambdaCode } from './code'; -import { FunctionName, LambdaRef } from './lambda-ref'; -import { LambdaVersion } from './lambda-version'; +import { Code } from './code'; +import { FunctionName, FunctionRef } from './lambda-ref'; +import { FunctionVersion } from './lambda-version'; import { cloudformation, FunctionArn } from './lambda.generated'; -import { LambdaRuntime } from './runtime'; +import { Runtime } from './runtime'; -export interface LambdaProps { +export interface FunctionProps { /** * The source code of your Lambda function. You can point to a file in an * Amazon Simple Storage Service (Amazon S3) bucket or specify your source * code as inline text. */ - code: LambdaCode; + code: Code; /** * A description of the function. @@ -52,7 +52,7 @@ export interface LambdaProps { * For valid values, see the Runtime property in the AWS Lambda Developer * Guide. */ - runtime: LambdaRuntime; + runtime: Runtime; /** * A name for the function. If you don't specify a name, AWS CloudFormation @@ -102,7 +102,7 @@ export interface LambdaProps { * This construct does not yet reproduce all features from the underlying resource * library. */ -export class Lambda extends LambdaRef { +export class Function extends FunctionRef { /** * Name of this function */ @@ -121,7 +121,7 @@ export class Lambda extends LambdaRef { /** * The runtime configured for this lambda. */ - public readonly runtime: LambdaRuntime; + public readonly runtime: Runtime; /** * The name of the handler configured for this lambda. @@ -135,7 +135,7 @@ export class Lambda extends LambdaRef { */ private readonly environment?: { [key: string]: any }; - constructor(parent: cdk.Construct, name: string, props: LambdaProps) { + constructor(parent: cdk.Construct, name: string, props: FunctionProps) { super(parent, name); this.environment = props.environment || { }; @@ -209,8 +209,8 @@ export class Lambda extends LambdaRef { * @param description A description for this version. * @returns A new Version object. */ - public addVersion(name: string, codeSha256?: string, description?: string): LambdaVersion { - return new LambdaVersion(this, 'Version' + name, { + public addVersion(name: string, codeSha256?: string, description?: string): FunctionVersion { + return new FunctionVersion(this, 'Version' + name, { lambda: this, codeSha256, description, diff --git a/packages/@aws-cdk/aws-lambda/lib/permission.ts b/packages/@aws-cdk/aws-lambda/lib/permission.ts index 95984b97a1f75..d4b6215638832 100644 --- a/packages/@aws-cdk/aws-lambda/lib/permission.ts +++ b/packages/@aws-cdk/aws-lambda/lib/permission.ts @@ -4,7 +4,7 @@ import { Arn, PolicyPrincipal } from '@aws-cdk/cdk'; * Represents a permission statement that can be added to a Lambda's resource policy * via the `addToResourcePolicy` method. */ -export interface LambdaPermission { +export interface Permission { /** * The Lambda actions that you want to allow in this statement. For example, * you can specify lambda:CreateFunction to specify a certain action, or use diff --git a/packages/@aws-cdk/aws-lambda/lib/runtime.ts b/packages/@aws-cdk/aws-lambda/lib/runtime.ts index 7e8ec83e9deb6..4163c6bc608dc 100644 --- a/packages/@aws-cdk/aws-lambda/lib/runtime.ts +++ b/packages/@aws-cdk/aws-lambda/lib/runtime.ts @@ -9,23 +9,23 @@ export interface LambdaRuntimeProps { /** * Lambda function runtime environment. */ -export class LambdaRuntime implements InlinableLambdaRuntime, InlinableJavascriptLambdaRuntime { - public static readonly NodeJS = new LambdaRuntime('nodejs', { supportsInlineCode: true }) as InlinableJavascriptLambdaRuntime; +export class Runtime implements InlinableRuntime, InlinableJavaScriptRuntime { + public static readonly NodeJS = new Runtime('nodejs', { supportsInlineCode: true }) as InlinableJavaScriptRuntime; // Using ``as InlinableLambdaRuntime`` because that class cannot be defined just yet - public static readonly NodeJS43 = new LambdaRuntime('nodejs4.3', { supportsInlineCode: true }) as InlinableJavascriptLambdaRuntime; - public static readonly NodeJS43Edge = new LambdaRuntime('nodejs4.3-edge'); + public static readonly NodeJS43 = new Runtime('nodejs4.3', { supportsInlineCode: true }) as InlinableJavaScriptRuntime; + public static readonly NodeJS43Edge = new Runtime('nodejs4.3-edge'); // Using ``as InlinableLambdaRuntime`` because that class cannot be defined just yet - public static readonly NodeJS610 = new LambdaRuntime('nodejs6.10', { supportsInlineCode: true }) as InlinableJavascriptLambdaRuntime; - public static readonly NodeJS810 = new LambdaRuntime('nodejs8.10'); - public static readonly Java8 = new LambdaRuntime('java8'); + public static readonly NodeJS610 = new Runtime('nodejs6.10', { supportsInlineCode: true }) as InlinableJavaScriptRuntime; + public static readonly NodeJS810 = new Runtime('nodejs8.10'); + public static readonly Java8 = new Runtime('java8'); // Using ``as InlinableLambdaRuntime`` because that class cannot be defined just yet - public static readonly Python27 = new LambdaRuntime('python2.7', { supportsInlineCode: true }) as InlinableLambdaRuntime; + public static readonly Python27 = new Runtime('python2.7', { supportsInlineCode: true }) as InlinableRuntime; // Using ``as InlinableLambdaRuntime`` because that class cannot be defined just yet - public static readonly Python36 = new LambdaRuntime('python3.6', { supportsInlineCode: true }) as InlinableLambdaRuntime; - public static readonly DotNetCore1 = new LambdaRuntime('dotnetcore1.0'); - public static readonly DotNetCore2 = new LambdaRuntime('dotnetcore2.0'); - public static readonly DotNetCore21 = new LambdaRuntime('dotnetcore2.1'); - public static readonly Go1x = new LambdaRuntime('go1.x'); + public static readonly Python36 = new Runtime('python3.6', { supportsInlineCode: true }) as InlinableRuntime; + public static readonly DotNetCore1 = new Runtime('dotnetcore1.0'); + public static readonly DotNetCore2 = new Runtime('dotnetcore2.0'); + public static readonly DotNetCore21 = new Runtime('dotnetcore2.1'); + public static readonly Go1x = new Runtime('go1.x'); /** The name of this runtime, as expected by the Lambda resource. */ public readonly name: string; @@ -46,7 +46,7 @@ export class LambdaRuntime implements InlinableLambdaRuntime, InlinableJavascrip * A ``LambdaRuntime`` that can be used in conjunction with the ``ZipFile`` * property of the ``AWS::Lambda::Function`` resource. */ -export interface InlinableLambdaRuntime { +export interface InlinableRuntime { readonly name: string; readonly supportsInlineCode: boolean; } @@ -55,4 +55,4 @@ export interface InlinableLambdaRuntime { * A ``LambdaRuntime`` that can be used for inlining JavaScript. */ // tslint:disable-next-line:no-empty-interface this is a marker to allow type-safe declarations -export interface InlinableJavascriptLambdaRuntime extends InlinableLambdaRuntime {} +export interface InlinableJavaScriptRuntime extends InlinableRuntime {} diff --git a/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts b/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts index 6eec310226037..226f69b733ddf 100644 --- a/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts +++ b/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts @@ -1,14 +1,14 @@ import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); -import { Lambda, LambdaProps } from './lambda'; -import { FunctionName, LambdaRef } from './lambda-ref'; +import { Function, FunctionProps } from './lambda'; +import { FunctionName, FunctionRef } from './lambda-ref'; import { FunctionArn } from './lambda.generated'; -import { LambdaPermission } from './permission'; +import { Permission } from './permission'; /** * Properties for a newly created singleton Lambda */ -export interface SingletonLambdaProps extends LambdaProps { +export interface SingletonFunctionProps extends FunctionProps { /** * A unique identifier to identify this lambda * @@ -35,14 +35,14 @@ export interface SingletonLambdaProps extends LambdaProps { * The lambda is identified using the value of 'uuid'. Run 'uuidgen' * for every SingletonLambda you create. */ -export class SingletonLambda extends LambdaRef { +export class SingletonFunction extends FunctionRef { public readonly functionName: FunctionName; public readonly functionArn: FunctionArn; public readonly role?: iam.Role | undefined; protected readonly canCreatePermissions: boolean; - private lambdaFunction: LambdaRef; + private lambdaFunction: FunctionRef; - constructor(parent: cdk.Construct, name: string, props: SingletonLambdaProps) { + constructor(parent: cdk.Construct, name: string, props: SingletonFunctionProps) { super(parent, name); this.lambdaFunction = this.ensureLambda(props); @@ -54,20 +54,20 @@ export class SingletonLambda extends LambdaRef { this.canCreatePermissions = true; // Doesn't matter, addPermission is overriden anyway } - public addPermission(name: string, permission: LambdaPermission) { + public addPermission(name: string, permission: Permission) { return this.lambdaFunction.addPermission(name, permission); } - private ensureLambda(props: SingletonLambdaProps): LambdaRef { + private ensureLambda(props: SingletonFunctionProps): FunctionRef { const constructName = (props.lambdaPurpose || 'SingletonLambda') + slugify(props.uuid); const stack = cdk.Stack.find(this); const existing = stack.tryFindChild(constructName); if (existing) { // Just assume this is true - return existing as LambdaRef; + return existing as FunctionRef; } - return new Lambda(stack, constructName, props); + return new Function(stack, constructName, props); } } diff --git a/packages/@aws-cdk/aws-lambda/test/integ.assets.file.ts b/packages/@aws-cdk/aws-lambda/test/integ.assets.file.ts index cf74288ca61b7..96ce0a6d10824 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.assets.file.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.assets.file.ts @@ -1,16 +1,16 @@ import cdk = require('@aws-cdk/cdk'); import path = require('path'); -import { Lambda, LambdaCode, LambdaRuntime } from '../lib'; +import lambda = require('../lib'); class TestStack extends cdk.Stack { constructor(parent: cdk.App, id: string) { super(parent, id); /// !show - new Lambda(this, 'MyLambda', { - code: LambdaCode.file(path.join(__dirname, 'handler.zip')), + new lambda.Function(this, 'MyLambda', { + code: lambda.Code.file(path.join(__dirname, 'handler.zip')), handler: 'index.main', - runtime: LambdaRuntime.Python36 + runtime: lambda.Runtime.Python36 }); /// !hide } diff --git a/packages/@aws-cdk/aws-lambda/test/integ.assets.lit.ts b/packages/@aws-cdk/aws-lambda/test/integ.assets.lit.ts index 2ae4005806feb..f8051aef52055 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.assets.lit.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.assets.lit.ts @@ -1,16 +1,16 @@ import cdk = require('@aws-cdk/cdk'); import path = require('path'); -import { Lambda, LambdaCode, LambdaRuntime } from '../lib'; +import lambda = require('../lib'); class TestStack extends cdk.Stack { constructor(parent: cdk.App, id: string) { super(parent, id); /// !show - new Lambda(this, 'MyLambda', { - code: LambdaCode.directory(path.join(__dirname, 'my-lambda-handler')), + new lambda.Function(this, 'MyLambda', { + code: lambda.Code.directory(path.join(__dirname, 'my-lambda-handler')), handler: 'index.main', - runtime: LambdaRuntime.Python36 + runtime: lambda.Runtime.Python36 }); /// !hide } diff --git a/packages/@aws-cdk/aws-lambda/test/integ.inline.ts b/packages/@aws-cdk/aws-lambda/test/integ.inline.ts index 55a252170fef6..7117b303a8193 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.inline.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.inline.ts @@ -1,6 +1,6 @@ import s3 = require('@aws-cdk/aws-s3'); import cdk = require('@aws-cdk/cdk'); -import { InlineJavaScriptLambda } from '../lib/inline'; +import lambda = require('../lib'); const app = new cdk.App(process.argv); @@ -8,7 +8,7 @@ const stack = new cdk.Stack(app, 'aws-cdk-lambda-2'); const bucket = new s3.Bucket(stack, 'MyBucket'); -const lambda = new InlineJavaScriptLambda(stack, 'MyLambda', { +const fn = new lambda.InlineJavaScriptFunction(stack, 'MyLambda', { environment: { BUCKET_NAME: bucket.bucketName }, @@ -34,6 +34,6 @@ const lambda = new InlineJavaScriptLambda(stack, 'MyLambda', { } }); -bucket.grantReadWrite(lambda.role); +bucket.grantReadWrite(fn.role); process.stdout.write(app.run()); diff --git a/packages/@aws-cdk/aws-lambda/test/integ.lambda.ts b/packages/@aws-cdk/aws-lambda/test/integ.lambda.ts index eab9db265bf42..3ca2aac0bc342 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.lambda.ts @@ -1,21 +1,21 @@ -import { App, PolicyStatement, Stack } from '@aws-cdk/cdk'; -import { Alias, Lambda, LambdaInlineCode, LambdaRuntime } from '../lib'; +import cdk = require('@aws-cdk/cdk'); +import lambda = require('../lib'); -const app = new App(process.argv); +const app = new cdk.App(process.argv); -const stack = new Stack(app, 'aws-cdk-lambda-1'); +const stack = new cdk.Stack(app, 'aws-cdk-lambda-1'); -const fn = new Lambda(stack, 'MyLambda', { - code: new LambdaInlineCode('foo'), +const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('foo'), handler: 'index.handler', - runtime: LambdaRuntime.NodeJS610, + runtime: lambda.Runtime.NodeJS610, }); -fn.addToRolePolicy(new PolicyStatement().addResource('*').addAction('*')); +fn.addToRolePolicy(new cdk.PolicyStatement().addResource('*').addAction('*')); const version = fn.addVersion('1'); -new Alias(stack, 'Alias', { +new lambda.Alias(stack, 'Alias', { aliasName: 'prod', version, }); diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index 93babd32dc244..16d76502e0966 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -1,20 +1,20 @@ import { beASupersetOfTemplate, expect, haveResource } from '@aws-cdk/assert'; import { AccountPrincipal, resolve, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; -import { Alias, Lambda, LambdaInlineCode, LambdaRuntime } from '../lib'; +import lambda = require('../lib'); export = { 'version and aliases'(test: Test): void { const stack = new Stack(); - const lambda = new Lambda(stack, 'MyLambda', { - code: new LambdaInlineCode('hello()'), + const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('hello()'), handler: 'index.hello', - runtime: LambdaRuntime.NodeJS610, + runtime: lambda.Runtime.NodeJS610, }); - const version = lambda.addVersion('1'); + const version = fn.addVersion('1'); - new Alias(stack, 'Alias', { + new lambda.Alias(stack, 'Alias', { aliasName: 'prod', version, }); @@ -42,16 +42,16 @@ export = { 'can add additional versions to alias'(test: Test) { const stack = new Stack(); - const lambda = new Lambda(stack, 'MyLambda', { - code: new LambdaInlineCode('hello()'), + const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('hello()'), handler: 'index.hello', - runtime: LambdaRuntime.NodeJS610, + runtime: lambda.Runtime.NodeJS610, }); - const version1 = lambda.addVersion('1'); - const version2 = lambda.addVersion('2'); + const version1 = fn.addVersion('1'); + const version2 = fn.addVersion('2'); - new Alias(stack, 'Alias', { + new lambda.Alias(stack, 'Alias', { aliasName: 'prod', version: version1, additionalVersions: [{ version: version2, weight: 0.1 }] @@ -75,17 +75,17 @@ export = { 'sanity checks on version weights'(test: Test) { const stack = new Stack(); - const lambda = new Lambda(stack, 'MyLambda', { - code: new LambdaInlineCode('hello()'), + const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('hello()'), handler: 'index.hello', - runtime: LambdaRuntime.NodeJS610, + runtime: lambda.Runtime.NodeJS610, }); - const version = lambda.addVersion('1'); + const version = fn.addVersion('1'); // WHEN: Individual weight too high test.throws(() => { - new Alias(stack, 'Alias1', { + new lambda.Alias(stack, 'Alias1', { aliasName: 'prod', version, additionalVersions: [{ version, weight: 5 }] }); @@ -93,7 +93,7 @@ export = { // WHEN: Sum too high test.throws(() => { - new Alias(stack, 'Alias2', { + new lambda.Alias(stack, 'Alias2', { aliasName: 'prod', version, additionalVersions: [{ version, weight: 0.5 }, { version, weight: 0.6 }] }); @@ -106,14 +106,14 @@ export = { const stack = new Stack(); // GIVEN - const lambda = new Lambda(stack, 'MyLambda', { - code: new LambdaInlineCode('hello()'), + const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('hello()'), handler: 'index.hello', - runtime: LambdaRuntime.NodeJS610, + runtime: lambda.Runtime.NodeJS610, }); - const version = lambda.addVersion('1'); - const alias = new Alias(stack, 'Alias', { aliasName: 'prod', version }); + const version = fn.addVersion('1'); + const alias = new lambda.Alias(stack, 'Alias', { aliasName: 'prod', version }); // WHEN alias.addPermission('Perm', { @@ -122,7 +122,7 @@ export = { // THEN expect(stack).to(haveResource('AWS::Lambda::Permission', { - FunctionName: resolve(lambda.functionName), + FunctionName: resolve(fn.functionName), Principal: "123456" })); diff --git a/packages/@aws-cdk/aws-lambda/test/test.inline.ts b/packages/@aws-cdk/aws-lambda/test/test.inline.ts index 077fff6e65fad..508dfcd894622 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.inline.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.inline.ts @@ -4,14 +4,14 @@ import cdk = require('@aws-cdk/cdk'); import fs = require('fs'); import { Test } from 'nodeunit'; import path = require('path'); -import { InlineJavaScriptLambda } from '../lib'; +import { InlineJavaScriptFunction } from '../lib'; export = { 'inline node lambda allows plugging in javascript functions as handlers'(test: Test) { const stack = new cdk.Stack(); const bucket = new s3.Bucket(stack, 'MyBucket'); - const lambda = new InlineJavaScriptLambda(stack, 'MyLambda', { + const lambda = new InlineJavaScriptFunction(stack, 'MyLambda', { environment: { BUCKET_NAME: bucket.bucketName }, diff --git a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts index 1edd36e3816a9..095a3ecd08d0e 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts @@ -4,7 +4,7 @@ import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; import path = require('path'); -import { Lambda, LambdaCode, LambdaInlineCode, LambdaRef, LambdaRuntime } from '../lib'; +import lambda = require('../lib'); // tslint:disable:object-literal-key-quotes @@ -12,10 +12,10 @@ export = { 'default function'(test: Test) { const stack = new cdk.Stack(); - new Lambda(stack, 'MyLambda', { - code: new LambdaInlineCode('foo'), + new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('foo'), handler: 'index.handler', - runtime: LambdaRuntime.NodeJS610, + runtime: lambda.Runtime.NodeJS610, }); expect(stack).toMatch({ Resources: @@ -46,10 +46,10 @@ export = { 'adds policy permissions'(test: Test) { const stack = new cdk.Stack(); - new Lambda(stack, 'MyLambda', { - code: new LambdaInlineCode('foo'), + new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('foo'), handler: 'index.handler', - runtime: LambdaRuntime.NodeJS610, + runtime: lambda.Runtime.NodeJS610, initialPolicy: [new cdk.PolicyStatement().addAction("*").addResource("*")] }); expect(stack).toMatch({ Resources: @@ -101,10 +101,10 @@ export = { 'fails if inline code is used for an invalid runtime'(test: Test) { const stack = new cdk.Stack(); - test.throws(() => new Lambda(stack, 'MyLambda', { - code: new LambdaInlineCode('foo'), + test.throws(() => new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('foo'), handler: 'bar', - runtime: LambdaRuntime.DotNetCore2 + runtime: lambda.Runtime.DotNetCore2 })); test.done(); }, @@ -112,9 +112,9 @@ export = { 'addToResourcePolicy': { 'can be used to add permissions to the Lambda function'(test: Test) { const stack = new cdk.Stack(); - const lambda = newTestLambda(stack); + const fn = newTestLambda(stack); - lambda.addPermission('S3Permission', { + fn.addPermission('S3Permission', { action: 'lambda:*', principal: new cdk.ServicePrincipal('s3.amazonaws.com'), sourceAccount: new cdk.AwsAccountId(), @@ -184,13 +184,13 @@ export = { 'fails if the principal is not a service or account principals'(test: Test) { const stack = new cdk.Stack(); - const lambda = newTestLambda(stack); + const fn = newTestLambda(stack); - test.throws(() => lambda.addPermission('F1', { principal: new cdk.ArnPrincipal('just:arn') }), + test.throws(() => fn.addPermission('F1', { principal: new cdk.ArnPrincipal('just:arn') }), /Invalid principal type for Lambda permission statement/); - lambda.addPermission('S1', { principal: new cdk.ServicePrincipal('my-service') }); - lambda.addPermission('S2', { principal: new cdk.AccountPrincipal('account') }); + fn.addPermission('S1', { principal: new cdk.ServicePrincipal('my-service') }); + fn.addPermission('S2', { principal: new cdk.AccountPrincipal('account') }); test.done(); }, @@ -204,9 +204,9 @@ export = { role.addToPolicy(new cdk.PolicyStatement().addAction('confirm:itsthesame')); // WHEN - const fn = new Lambda(stack, 'Function', { - code: new LambdaInlineCode('test'), - runtime: LambdaRuntime.Python36, + const fn = new lambda.Function(stack, 'Function', { + code: new lambda.InlineCode('test'), + runtime: lambda.Runtime.Python36, handler: 'index.test', role, initialPolicy: [ @@ -236,11 +236,11 @@ export = { // GIVEN const stack1 = new cdk.Stack(); const stack2 = new cdk.Stack(); - const lambda = newTestLambda(stack1); + const fn = newTestLambda(stack1); // WHEN - const props = lambda.export(); - const imported = LambdaRef.import(stack2, 'Imported', props); + const props = fn.export(); + const imported = lambda.FunctionRef.import(stack2, 'Imported', props); // Can call addPermission() but it won't do anything imported.addPermission('Hello', { @@ -254,11 +254,11 @@ export = { 'Lambda can serve as EventRule target, permission gets added'(test: Test) { // GIVEN const stack = new cdk.Stack(); - const lambda = newTestLambda(stack); + const fn = newTestLambda(stack); const rule = new events.EventRule(stack, 'Rule'); // WHEN - rule.addTarget(lambda); + rule.addTarget(fn); // THEN const lambdaId = "MyLambdaCCE802FB"; @@ -284,10 +284,10 @@ export = { 'Lambda code can be read from a local directory via an asset'(test: Test) { // GIVEN const stack = new cdk.Stack(); - new Lambda(stack, 'MyLambda', { - code: LambdaCode.directory(path.join(__dirname, 'my-lambda-handler')), + new lambda.Function(stack, 'MyLambda', { + code: lambda.Code.directory(path.join(__dirname, 'my-lambda-handler')), handler: 'index.handler', - runtime: LambdaRuntime.Python36 + runtime: lambda.Runtime.Python36 }); // THEN @@ -315,9 +315,9 @@ export = { }; function newTestLambda(parent: cdk.Construct) { - return new Lambda(parent, 'MyLambda', { - code: new LambdaInlineCode('foo'), + return new lambda.Function(parent, 'MyLambda', { + code: new lambda.InlineCode('foo'), handler: 'bar', - runtime: LambdaRuntime.Python27 + runtime: lambda.Runtime.Python27 }); } diff --git a/packages/@aws-cdk/aws-lambda/test/test.singleton-lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.singleton-lambda.ts index 51f9c09f65fc3..18a706f10982b 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.singleton-lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.singleton-lambda.ts @@ -10,10 +10,10 @@ export = { // WHEN for (let i = 0; i < 5; i++) { - new lambda.SingletonLambda(stack, `Singleton${i}`, { + new lambda.SingletonFunction(stack, `Singleton${i}`, { uuid: '84c0de93-353f-4217-9b0b-45b6c993251a', - code: new lambda.LambdaInlineCode('def hello(): pass'), - runtime: lambda.LambdaRuntime.Python27, + code: new lambda.InlineCode('def hello(): pass'), + runtime: lambda.Runtime.Python27, handler: 'index.hello', timeout: 300, }); diff --git a/packages/@aws-cdk/aws-lambda/test/test.subscriptiondestination.ts b/packages/@aws-cdk/aws-lambda/test/test.subscriptiondestination.ts index 1b85ae9fafae3..fd6d44dd80179 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.subscriptiondestination.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.subscriptiondestination.ts @@ -2,23 +2,23 @@ import { expect, haveResource } from '@aws-cdk/assert'; import logs = require('@aws-cdk/aws-logs'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; -import { Lambda, LambdaInlineCode, LambdaRuntime } from '../lib'; +import lambda = require('../lib'); export = { 'lambda can be used as metric subscription destination'(test: Test) { // GIVEN const stack = new cdk.Stack(); - const lambda = new Lambda(stack, 'MyLambda', { - code: new LambdaInlineCode('foo'), + const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('foo'), handler: 'index.handler', - runtime: LambdaRuntime.NodeJS610, + runtime: lambda.Runtime.NodeJS610, }); const logGroup = new logs.LogGroup(stack, 'LogGroup'); // WHEN new logs.SubscriptionFilter(stack, 'Subscription', { logGroup, - destination: lambda, + destination: fn, filterPattern: logs.FilterPattern.allEvents() }); diff --git a/packages/@aws-cdk/aws-logs/README.md b/packages/@aws-cdk/aws-logs/README.md index 65d69e6dd20d2..1f223bc6b26de 100644 --- a/packages/@aws-cdk/aws-logs/README.md +++ b/packages/@aws-cdk/aws-logs/README.md @@ -36,12 +36,12 @@ Create a `SubscriptionFilter`, initialize it with an appropriate `Pattern` (see below) and supply the intended destination: ```ts -const lambda = new Lambda(this, 'Lambda', { ... }); +const fn = new lambda.Function(this, 'Lambda', { ... }); const logGroup = new LogGroup(this, 'LogGroup', { ... }); new SubscriptionFilter(this, 'Subscription', { logGroup, - destination: lambda, + destination: fn, filterPattern: FilterPattern.allTerms("ERROR", "MainThread") }); ``` diff --git a/packages/@aws-cdk/aws-sns/lib/topic-ref.ts b/packages/@aws-cdk/aws-sns/lib/topic-ref.ts index 726bc90017da7..d12753da83ea6 100644 --- a/packages/@aws-cdk/aws-sns/lib/topic-ref.ts +++ b/packages/@aws-cdk/aws-sns/lib/topic-ref.ts @@ -109,7 +109,7 @@ export abstract class TopicRef extends cdk.Construct implements events.IEventRul * @param name A name for the subscription * @param lambdaFunction The Lambda function to invoke */ - public subscribeLambda(lambdaFunction: lambda.LambdaRef) { + public subscribeLambda(lambdaFunction: lambda.FunctionRef) { const subscriptionName = lambdaFunction.name + 'Subscription'; if (this.tryFindChild(subscriptionName)) { diff --git a/packages/@aws-cdk/aws-sns/test/integ.sns-lambda.ts b/packages/@aws-cdk/aws-sns/test/integ.sns-lambda.ts index ca1b1ef9379d5..c5cc6584d70e2 100644 --- a/packages/@aws-cdk/aws-sns/test/integ.sns-lambda.ts +++ b/packages/@aws-cdk/aws-sns/test/integ.sns-lambda.ts @@ -8,7 +8,7 @@ class SnsToSqs extends cdk.Stack { const topic = new sns.Topic(this, 'MyTopic'); - const fction = new lambda.InlineJavaScriptLambda(this, 'Echo', { + const fction = new lambda.InlineJavaScriptFunction(this, 'Echo', { handler: { fn: (event, _context, callback) => { // tslint:disable:no-console diff --git a/packages/@aws-cdk/aws-sns/test/test.sns.ts b/packages/@aws-cdk/aws-sns/test/test.sns.ts index b7dc38c05e7f2..fdaf02b8ea88b 100644 --- a/packages/@aws-cdk/aws-sns/test/test.sns.ts +++ b/packages/@aws-cdk/aws-sns/test/test.sns.ts @@ -215,7 +215,7 @@ export = { displayName: 'displayName' }); - const fction = new lambda.InlineJavaScriptLambda(stack, 'MyFunc', { + const fction = new lambda.InlineJavaScriptFunction(stack, 'MyFunc', { handler: { fn: (_event, _context, callback) => callback() } @@ -351,7 +351,7 @@ export = { }); const queue = new sqs.Queue(stack, 'MyQueue'); - const func = new lambda.InlineJavaScriptLambda(stack, 'MyLambda', { + const func = new lambda.InlineJavaScriptFunction(stack, 'MyLambda', { handler: { fn: (_event, _context, callback: any) => callback() } diff --git a/packages/@aws-cdk/runtime-values/test/integ.rtv.lambda.ts b/packages/@aws-cdk/runtime-values/test/integ.rtv.lambda.ts index f399e38257ee5..77dd56beab008 100644 --- a/packages/@aws-cdk/runtime-values/test/integ.rtv.lambda.ts +++ b/packages/@aws-cdk/runtime-values/test/integ.rtv.lambda.ts @@ -12,7 +12,7 @@ class TestStack extends cdk.Stack { super(parent, name); const queue = new sqs.Queue(this, 'MyQueue'); - const fn = new lambda.InlineJavaScriptLambda(this, 'MyFunction', { + const fn = new lambda.InlineJavaScriptFunction(this, 'MyFunction', { handler: { fn: runtimeCode }, }); diff --git a/packages/aws-cdk-all/package.json b/packages/aws-cdk-all/package.json deleted file mode 100644 index 847e33b641a62..0000000000000 --- a/packages/aws-cdk-all/package.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "name": "aws-cdk-all", - "private": true, - "version": "0.8.0", - "description": "A single package that provides all of the CDK dependencies", - "main": "index.js", - "types": "index.d.ts", - "repository": { - "type": "git", - "url": "git://github.com/awslabs/aws-cdk" - }, - "author": { - "name": "Amazon Web Services", - "url": "https://aws.amazon.com" - }, - "scripts": { - "build": "echo Nothing to do", - "test": "echo Nothing to do" - }, - "license": "Apache-2.0", - "dependencies": { - "@aws-cdk/aws-apigateway": "^0.8.0", - "@aws-cdk/aws-applicationautoscaling": "^0.8.0", - "@aws-cdk/aws-appsync": "^0.8.0", - "@aws-cdk/aws-athena": "^0.8.0", - "@aws-cdk/aws-autoscaling": "^0.8.0", - "@aws-cdk/aws-autoscalingplans": "^0.8.0", - "@aws-cdk/aws-batch": "^0.8.0", - "@aws-cdk/aws-budgets": "^0.8.0", - "@aws-cdk/aws-certificatemanager": "^0.8.0", - "@aws-cdk/aws-cloud9": "^0.8.0", - "@aws-cdk/aws-cloudformation": "^0.8.0", - "@aws-cdk/aws-cloudfront": "^0.8.0", - "@aws-cdk/aws-cloudtrail": "^0.8.0", - "@aws-cdk/aws-cloudwatch": "^0.8.0", - "@aws-cdk/aws-codebuild": "^0.8.0", - "@aws-cdk/aws-codecommit": "^0.8.0", - "@aws-cdk/aws-codedeploy": "^0.8.0", - "@aws-cdk/aws-codepipeline": "^0.8.0", - "@aws-cdk/aws-cognito": "^0.8.0", - "@aws-cdk/aws-config": "^0.8.0", - "@aws-cdk/aws-datapipeline": "^0.8.0", - "@aws-cdk/aws-dax": "^0.8.0", - "@aws-cdk/aws-directoryservice": "^0.8.0", - "@aws-cdk/aws-dms": "^0.8.0", - "@aws-cdk/aws-dynamodb": "^0.8.0", - "@aws-cdk/aws-ec2": "^0.8.0", - "@aws-cdk/aws-ecr": "^0.8.0", - "@aws-cdk/aws-ecs": "^0.8.0", - "@aws-cdk/aws-efs": "^0.8.0", - "@aws-cdk/aws-eks": "^0.8.0", - "@aws-cdk/aws-elasticache": "^0.8.0", - "@aws-cdk/aws-elasticbeanstalk": "^0.8.0", - "@aws-cdk/aws-elasticloadbalancing": "^0.8.0", - "@aws-cdk/aws-elasticloadbalancingv2": "^0.8.0", - "@aws-cdk/aws-elasticsearch": "^0.8.0", - "@aws-cdk/aws-emr": "^0.8.0", - "@aws-cdk/aws-events": "^0.8.0", - "@aws-cdk/aws-gamelift": "^0.8.0", - "@aws-cdk/aws-glue": "^0.8.0", - "@aws-cdk/aws-guardduty": "^0.8.0", - "@aws-cdk/aws-iam": "^0.8.0", - "@aws-cdk/aws-inspector": "^0.8.0", - "@aws-cdk/aws-iot": "^0.8.0", - "@aws-cdk/aws-kinesis": "^0.8.0", - "@aws-cdk/aws-kinesisanalytics": "^0.8.0", - "@aws-cdk/aws-kinesisfirehose": "^0.8.0", - "@aws-cdk/aws-kms": "^0.8.0", - "@aws-cdk/aws-lambda": "^0.8.0", - "@aws-cdk/aws-logs": "^0.8.0", - "@aws-cdk/aws-neptune": "^0.8.0", - "@aws-cdk/aws-opsworks": "^0.8.0", - "@aws-cdk/aws-rds": "^0.8.0", - "@aws-cdk/aws-redshift": "^0.8.0", - "@aws-cdk/aws-route53": "^0.8.0", - "@aws-cdk/aws-s3": "^0.8.0", - "@aws-cdk/aws-sdb": "^0.8.0", - "@aws-cdk/aws-serverless": "^0.8.0", - "@aws-cdk/aws-servicecatalog": "^0.8.0", - "@aws-cdk/aws-servicediscovery": "^0.8.0", - "@aws-cdk/aws-ses": "^0.8.0", - "@aws-cdk/aws-sns": "^0.8.0", - "@aws-cdk/aws-sqs": "^0.8.0", - "@aws-cdk/aws-ssm": "^0.8.0", - "@aws-cdk/aws-stepfunctions": "^0.8.0", - "@aws-cdk/aws-waf": "^0.8.0", - "@aws-cdk/aws-wafregional": "^0.8.0", - "@aws-cdk/aws-workspaces": "^0.8.0", - "@aws-cdk/cdk": "^0.8.0", - "@aws-cdk/cx-api": "^0.8.0", - "@aws-cdk/runtime-values": "^0.8.0" - }, - "keywords": [ - "aws", - "cdk" - ], - "pkglint": { - "ignore": true - } -} From a283a392ac5c9035c5d38a7e40c21bb8131f5692 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Mon, 13 Aug 2018 12:25:32 +0300 Subject: [PATCH 05/33] feat(aws-iam): support maxSessionDuration for Role (#545) Allow specifying a maximum session duration for roles. Fixes #543 --- packages/@aws-cdk/aws-iam/lib/role.ts | 34 ++++++++++++ packages/@aws-cdk/aws-iam/test/test.role.ts | 59 +++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/packages/@aws-cdk/aws-iam/lib/role.ts b/packages/@aws-cdk/aws-iam/lib/role.ts index 4d4d079febf00..260f99f02bb60 100644 --- a/packages/@aws-cdk/aws-iam/lib/role.ts +++ b/packages/@aws-cdk/aws-iam/lib/role.ts @@ -41,6 +41,27 @@ export interface RoleProps { * Acknowledging IAM Resources in AWS CloudFormation Templates. */ roleName?: string; + + /** + * The maximum session duration (in seconds) that you want to set for the + * specified role. If you do not specify a value for this setting, the + * default maximum of one hour is applied. This setting can have a value + * from 1 hour (3600sec) to 12 (43200sec) hours. + * + * Anyone who assumes the role from the AWS CLI or API can use the + * DurationSeconds API parameter or the duration-seconds CLI parameter to + * request a longer session. The MaxSessionDuration setting determines the + * maximum duration that can be requested using the DurationSeconds + * parameter. + * + * If users don't specify a value for the DurationSeconds parameter, their + * security credentials are valid for one hour by default. This applies when + * you use the AssumeRole* API operations or the assume-role* CLI operations + * but does not apply when you use those operations to create a console URL. + * + * @link https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html + */ + maxSessionDurationSec?: number; } /** @@ -85,11 +106,14 @@ export class Role extends Construct implements IIdentityResource, IPrincipal, ID this.assumeRolePolicy = createAssumeRolePolicy(props.assumedBy); this.managedPolicies = props.managedPolicyArns || [ ]; + validateMaxSessionDuration(props.maxSessionDurationSec); + const role = new cloudformation.RoleResource(this, 'Resource', { assumeRolePolicyDocument: this.assumeRolePolicy as any, managedPolicyArns: undefinedIfEmpty(() => this.managedPolicies), path: props.path, roleName: props.roleName, + maxSessionDuration: props.maxSessionDurationSec }); this.roleArn = role.roleArn; @@ -140,3 +164,13 @@ function createAssumeRolePolicy(principal: PolicyPrincipal) { .addPrincipal(principal) .addAction(principal.assumeRoleAction)); } + +function validateMaxSessionDuration(duration?: number) { + if (duration === undefined) { + return; + } + + if (duration < 3600 || duration > 43200) { + throw new Error(`maxSessionDuration is set to ${duration}, but must be >= 3600sec (1hr) and <= 43200sec (12hrs)`); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iam/test/test.role.ts b/packages/@aws-cdk/aws-iam/test/test.role.ts index dfdef1d78f97c..98876bdf8d2a2 100644 --- a/packages/@aws-cdk/aws-iam/test/test.role.ts +++ b/packages/@aws-cdk/aws-iam/test/test.role.ts @@ -117,5 +117,64 @@ export = { })); test.done(); + }, + + 'maxSessionDuration': { + + 'is not specified by default'(test: Test) { + const stack = new Stack(); + new Role(stack, 'MyRole', { assumedBy: new ServicePrincipal('sns.amazonaws.com') }); + expect(stack).toMatch({ + Resources: { + MyRoleF48FFE04: { + Type: "AWS::IAM::Role", + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: "sts:AssumeRole", + Effect: "Allow", + Principal: { + Service: "sns.amazonaws.com" + } + } + ], + Version: "2012-10-17" + } + } + } + } + }); + test.done(); + }, + + 'can be used to specify the maximum session duration for assuming the role'(test: Test) { + const stack = new Stack(); + + new Role(stack, 'MyRole', { maxSessionDurationSec: 3700, assumedBy: new ServicePrincipal('sns.amazonaws.com') }); + + expect(stack).to(haveResource('AWS::IAM::Role', { + MaxSessionDuration: 3700 + })); + + test.done(); + }, + + 'must be between 3600 and 43200'(test: Test) { + const stack = new Stack(); + + const assumedBy = new ServicePrincipal('bla'); + + new Role(stack, 'MyRole1', { assumedBy, maxSessionDurationSec: 3600 }); + new Role(stack, 'MyRole2', { assumedBy, maxSessionDurationSec: 43200 }); + + const expected = (val: any) => `maxSessionDuration is set to ${val}, but must be >= 3600sec (1hr) and <= 43200sec (12hrs)`; + test.throws(() => new Role(stack, 'MyRole3', { assumedBy, maxSessionDurationSec: 60 }), expected(60)); + test.throws(() => new Role(stack, 'MyRole4', { assumedBy, maxSessionDurationSec: 3599 }), expected(3599)); + test.throws(() => new Role(stack, 'MyRole5', { assumedBy, maxSessionDurationSec: 43201 }), expected(43201)); + + test.done(); + } } + }; From 8cd07e62fc8b651e6bbdbc85553f2e6dc885016e Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Mon, 13 Aug 2018 19:09:09 +0300 Subject: [PATCH 06/33] feat(aws-s3): Bucket Notifications (#201) Adds support for S3 bucket notifications. The `bucket.onEvent` method will add a notification destination for a bucket. The s3.INotificationDestination interface is used to allow SNS, SQS and Lambda to implement notification destinations. This interface inverts the control and allows the destination to prepare to receive notifications. For example, it can modify it's policy appropriately. Since CloudFormation bucket notification support require two-phase deployments (due to the fact PutBucketNotification will fail if the destination policy has not been updated, and CloudFormation cannot create the policy until the bucket is created). The reason this is a limitation in CloudFormation is that they could not model the 1:1 relationship between the bucket and the notifications using the current semantics of CloudFormation. In the CDK, we can model this relationship by encapsulating the notifications custom resource behind a bucket. This means that users don't interact with this resource directly, but rather just subscribe to notifications on a bucket, and the resource (and accompanying handler) will be created as needed. --- packages/@aws-cdk/aws-s3/README.md | 37 +++ packages/@aws-cdk/aws-s3/lib/bucket.ts | 174 +++++++++++ packages/@aws-cdk/aws-s3/lib/index.ts | 1 + .../@aws-cdk/aws-s3/lib/notification-dest.ts | 37 +++ .../lib/notifications-resource/index.ts | 1 + .../notifications-resource-handler.ts | 165 +++++++++++ .../notifications-resource.ts | 172 +++++++++++ .../test/integ.notifications.expected.json | 273 ++++++++++++++++++ .../aws-s3/test/integ.notifications.ts | 20 ++ .../aws-s3/test/notification-dests.ts | 47 +++ .../aws-s3/test/test.notifications.ts | 272 +++++++++++++++++ 11 files changed, 1199 insertions(+) create mode 100644 packages/@aws-cdk/aws-s3/lib/notification-dest.ts create mode 100644 packages/@aws-cdk/aws-s3/lib/notifications-resource/index.ts create mode 100644 packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts create mode 100644 packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts create mode 100644 packages/@aws-cdk/aws-s3/test/integ.notifications.expected.json create mode 100644 packages/@aws-cdk/aws-s3/test/integ.notifications.ts create mode 100644 packages/@aws-cdk/aws-s3/test/notification-dests.ts create mode 100644 packages/@aws-cdk/aws-s3/test/test.notifications.ts diff --git a/packages/@aws-cdk/aws-s3/README.md b/packages/@aws-cdk/aws-s3/README.md index 2eaa3503204e6..4b51848ed16e8 100644 --- a/packages/@aws-cdk/aws-s3/README.md +++ b/packages/@aws-cdk/aws-s3/README.md @@ -143,3 +143,40 @@ new Consumer(app, 'consume', { process.stdout.write(app.run()); ``` + +### Bucket Notifications + +The Amazon S3 notification feature enables you to receive notifications when +certain events happen in your bucket as described under [S3 Bucket +Notifications] of the S3 Developer Guide. + +To subscribe for bucket notifications, use the `bucket.onEvent` method. The +`bucket.onObjectCreated` and `bucket.onObjectRemoved` can also be used for these +common use cases. + +The following example will subscribe an SNS topic to be notified of all +``s3:ObjectCreated:*` events: + +```ts +const myTopic = new sns.Topic(this, 'MyTopic'); +bucket.onEvent(s3.EventType.ObjectCreated, myTopic); +``` + +This call will also ensure that the topic policy can accept notifications for +this specific bucket. + +The following destinations are currently supported: + + * `sns.Topic` + * `sqs.Queue` + * `lambda.Function` + +It is also possible to specify S3 object key filters when subscribing. The +following example will notify `myQueue` when objects prefixed with `foo/` and +have the `.jpg` suffix are removed from the bucket. + +```ts +bucket.onEvent(s3.EventType.ObjectRemoved, myQueue, { prefix: 'foo/', suffix: '.jpg' }); +``` + +[S3 Bucket Notifications]: https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index ffa713df2bab8..b2f9fb390c47b 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -2,6 +2,8 @@ import iam = require('@aws-cdk/aws-iam'); import kms = require('@aws-cdk/aws-kms'); import cdk = require('@aws-cdk/cdk'); import { BucketPolicy } from './bucket-policy'; +import { IBucketNotificationDestination } from './notification-dest'; +import { BucketNotifications } from './notifications-resource'; import perms = require('./perms'); import { LifecycleRule } from './rule'; import { BucketArn, BucketDomainName, BucketDualStackDomainName, cloudformation } from './s3.generated'; @@ -289,6 +291,7 @@ export class Bucket extends BucketRef { protected autoCreatePolicy = true; private readonly lifecycleRules: LifecycleRule[] = []; private readonly versioned?: boolean; + private readonly notifications: BucketNotifications; constructor(parent: cdk.Construct, name: string, props: BucketProps = {}) { super(parent, name); @@ -316,6 +319,10 @@ export class Bucket extends BucketRef { // Add all lifecycle rules (props.lifecycleRules || []).forEach(this.addLifecycleRule.bind(this)); + + // defines a BucketNotifications construct. Notice that an actual resource will only + // be added if there are notifications added, so we don't need to condition this. + this.notifications = new BucketNotifications(this, 'Notifications', { bucket: this }); } /** @@ -333,6 +340,53 @@ export class Bucket extends BucketRef { this.lifecycleRules.push(rule); } + /** + * Adds a bucket notification event destination. + * @param event The event to trigger the notification + * @param dest The notification destination (Lambda, SNS Topic or SQS Queue) + * + * @param filters S3 object key filter rules to determine which objects + * trigger this event. Each filter must include a `prefix` and/or `suffix` + * that will be matched against the s3 object key. Refer to the S3 Developer Guide + * for details about allowed filter rules. + * + * @see https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html#notification-how-to-filtering + * + * @example + * + * bucket.onEvent(EventType.OnObjectCreated, myLambda, 'home/myusername/*') + * + * @see + * https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html + */ + public onEvent(event: EventType, dest: IBucketNotificationDestination, ...filters: NotificationKeyFilter[]) { + this.notifications.addNotification(event, dest, ...filters); + } + + /** + * Subscribes a destination to receive notificatins when an object is + * created in the bucket. This is identical to calling + * `onEvent(EventType.ObjectCreated)`. + * + * @param dest The notification destination (see onEvent) + * @param filters Filters (see onEvent) + */ + public onObjectCreated(dest: IBucketNotificationDestination, ...filters: NotificationKeyFilter[]) { + return this.onEvent(EventType.ObjectCreated, dest, ...filters); + } + + /** + * Subscribes a destination to receive notificatins when an object is + * removed from the bucket. This is identical to calling + * `onEvent(EventType.ObjectRemoved)`. + * + * @param dest The notification destination (see onEvent) + * @param filters Filters (see onEvent) + */ + public onObjectRemoved(dest: IBucketNotificationDestination, ...filters: NotificationKeyFilter[]) { + return this.onEvent(EventType.ObjectRemoved, dest, ...filters); + } + /** * Set up key properties and return the Bucket encryption property from the * user's configuration. @@ -485,6 +539,126 @@ export class S3Url extends cdk.Token { } +/** + * Notification event types. + */ +export enum EventType { + /** + * Amazon S3 APIs such as PUT, POST, and COPY can create an object. Using + * these event types, you can enable notification when an object is created + * using a specific API, or you can use the s3:ObjectCreated:* event type to + * request notification regardless of the API that was used to create an + * object. + */ + ObjectCreated = 's3:ObjectCreated:*', + + /** + * Amazon S3 APIs such as PUT, POST, and COPY can create an object. Using + * these event types, you can enable notification when an object is created + * using a specific API, or you can use the s3:ObjectCreated:* event type to + * request notification regardless of the API that was used to create an + * object. + */ + ObjectCreatedPut = 's3:ObjectCreated:Put', + + /** + * Amazon S3 APIs such as PUT, POST, and COPY can create an object. Using + * these event types, you can enable notification when an object is created + * using a specific API, or you can use the s3:ObjectCreated:* event type to + * request notification regardless of the API that was used to create an + * object. + */ + ObjectCreatedPost = 's3:ObjectCreated:Post', + + /** + * Amazon S3 APIs such as PUT, POST, and COPY can create an object. Using + * these event types, you can enable notification when an object is created + * using a specific API, or you can use the s3:ObjectCreated:* event type to + * request notification regardless of the API that was used to create an + * object. + */ + ObjectCreatedCopy = 's3:ObjectCreated:Copy', + + /** + * Amazon S3 APIs such as PUT, POST, and COPY can create an object. Using + * these event types, you can enable notification when an object is created + * using a specific API, or you can use the s3:ObjectCreated:* event type to + * request notification regardless of the API that was used to create an + * object. + */ + ObjectCreatedCompleteMultipartUpload = 's3:ObjectCreated:CompleteMultipartUpload', + + /** + * By using the ObjectRemoved event types, you can enable notification when + * an object or a batch of objects is removed from a bucket. + * + * You can request notification when an object is deleted or a versioned + * object is permanently deleted by using the s3:ObjectRemoved:Delete event + * type. Or you can request notification when a delete marker is created for + * a versioned object by using s3:ObjectRemoved:DeleteMarkerCreated. For + * information about deleting versioned objects, see Deleting Object + * Versions. You can also use a wildcard s3:ObjectRemoved:* to request + * notification anytime an object is deleted. + * + * You will not receive event notifications from automatic deletes from + * lifecycle policies or from failed operations. + */ + ObjectRemoved = 's3:ObjectRemoved:*', + + /** + * By using the ObjectRemoved event types, you can enable notification when + * an object or a batch of objects is removed from a bucket. + * + * You can request notification when an object is deleted or a versioned + * object is permanently deleted by using the s3:ObjectRemoved:Delete event + * type. Or you can request notification when a delete marker is created for + * a versioned object by using s3:ObjectRemoved:DeleteMarkerCreated. For + * information about deleting versioned objects, see Deleting Object + * Versions. You can also use a wildcard s3:ObjectRemoved:* to request + * notification anytime an object is deleted. + * + * You will not receive event notifications from automatic deletes from + * lifecycle policies or from failed operations. + */ + ObjectRemovedDelete = 's3:ObjectRemoved:Delete', + + /** + * By using the ObjectRemoved event types, you can enable notification when + * an object or a batch of objects is removed from a bucket. + * + * You can request notification when an object is deleted or a versioned + * object is permanently deleted by using the s3:ObjectRemoved:Delete event + * type. Or you can request notification when a delete marker is created for + * a versioned object by using s3:ObjectRemoved:DeleteMarkerCreated. For + * information about deleting versioned objects, see Deleting Object + * Versions. You can also use a wildcard s3:ObjectRemoved:* to request + * notification anytime an object is deleted. + * + * You will not receive event notifications from automatic deletes from + * lifecycle policies or from failed operations. + */ + ObjectRemovedDeleteMarkerCreated = 's3:ObjectRemoved:DeleteMarkerCreated', + + /** + * You can use this event type to request Amazon S3 to send a notification + * message when Amazon S3 detects that an object of the RRS storage class is + * lost. + */ + ReducedRedundancyLostObject = 's3:ReducedRedundancyLostObject', +} + +export interface NotificationKeyFilter { + /** + * S3 keys must have the specified prefix. + */ + prefix?: string; + + /** + * S3 keys must have the specified suffix. + */ + suffix?: string; +} + class ImportedBucketRef extends BucketRef { public readonly bucketArn: BucketArn; public readonly bucketName: BucketName; diff --git a/packages/@aws-cdk/aws-s3/lib/index.ts b/packages/@aws-cdk/aws-s3/lib/index.ts index 593c797757b3f..e0d99e5e7ebe1 100644 --- a/packages/@aws-cdk/aws-s3/lib/index.ts +++ b/packages/@aws-cdk/aws-s3/lib/index.ts @@ -1,6 +1,7 @@ export * from './bucket'; export * from './bucket-policy'; export * from './rule'; +export * from './notification-dest'; // AWS::S3 CloudFormation Resources: export * from './s3.generated'; diff --git a/packages/@aws-cdk/aws-s3/lib/notification-dest.ts b/packages/@aws-cdk/aws-s3/lib/notification-dest.ts new file mode 100644 index 0000000000000..2e16e1aa1e7f3 --- /dev/null +++ b/packages/@aws-cdk/aws-s3/lib/notification-dest.ts @@ -0,0 +1,37 @@ +import cdk = require('@aws-cdk/cdk'); +import { Bucket } from './bucket'; + +/** + * Implemented by constructs that can be used as bucket notification destinations. + */ +export interface IBucketNotificationDestination { + /** + * Registers this resource to receive notifications for the specified bucket. + * @param bucket The bucket. Use the `path` of the bucket as a unique ID. + */ + asBucketNotificationDestination(bucket: Bucket): BucketNotificationDestinationProps; +} + +/** + * Represents the properties of a notification destination. + */ +export interface BucketNotificationDestinationProps { + /** + * The notification type. + */ + readonly type: BucketNotificationDestinationType; + + /** + * The ARN of the destination (i.e. Lambda, SNS, SQS). + */ + readonly arn: cdk.Arn; +} + +/** + * Supported types of notification destinations. + */ +export enum BucketNotificationDestinationType { + Lambda, + Queue, + Topic +} diff --git a/packages/@aws-cdk/aws-s3/lib/notifications-resource/index.ts b/packages/@aws-cdk/aws-s3/lib/notifications-resource/index.ts new file mode 100644 index 0000000000000..6cd00a3d115d2 --- /dev/null +++ b/packages/@aws-cdk/aws-s3/lib/notifications-resource/index.ts @@ -0,0 +1 @@ +export * from './notifications-resource'; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts new file mode 100644 index 0000000000000..209a85b3f52b3 --- /dev/null +++ b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource-handler.ts @@ -0,0 +1,165 @@ +import iam = require('@aws-cdk/aws-iam'); +import cdk = require('@aws-cdk/cdk'); + +/** + * A Lambda-based custom resource handler that provisions S3 bucket + * notifications for a bucket. + * + * The resource property schema is: + * + * { + * BucketName: string, NotificationConfiguration: { see + * PutBucketNotificationConfiguration } + * } + * + * For 'Delete' operations, we send an empty NotificationConfiguration as + * required. We propagate errors and results as-is. + * + * Sadly, we can't use @aws-cdk/aws-lambda as it will introduce a dependency + * cycle, so this uses raw `cdk.Resource`s. + */ +export class NotificationsResourceHandler extends cdk.Construct { + /** + * Defines a stack-singleton lambda function with the logic for a CloudFormation custom + * resource that provisions bucket notification configuration for a bucket. + * + * @returns The ARN of the custom resource lambda function. + */ + public static singleton(context: cdk.Construct) { + const root = cdk.Stack.find(context); + + // well-known logical id to ensure stack singletonity + const logicalId = 'BucketNotificationsHandler050a0587b7544547bf325f094a3db834'; + let lambda = root.tryFindChild(logicalId) as NotificationsResourceHandler; + if (!lambda) { + lambda = new NotificationsResourceHandler(root, logicalId); + } + + return lambda.functionArn; + } + + /** + * The ARN of the handler's lambda function. Used as a service token in the + * custom resource. + */ + public readonly functionArn: cdk.Arn; + + constructor(parent: cdk.Construct, id: string) { + super(parent, id); + + const role = new iam.Role(this, 'Role', { + assumedBy: new cdk.ServicePrincipal('lambda.amazonaws.com'), + managedPolicyArns: [ + cdk.Arn.fromComponents({ + service: 'iam', + region: '', // no region for managed policy + account: 'aws', // the account for a managed policy is 'aws' + resource: 'policy', + resourceName: 'service-role/AWSLambdaBasicExecutionRole', + }) + ] + }); + + // handler allows to put bucket notification on s3 buckets. + role.addToPolicy(new cdk.PolicyStatement() + .addAction('s3:PutBucketNotification') + .addResource('*')); + + const resource = new cdk.Resource(this, 'Resource', { + type: 'AWS::Lambda::Function', + properties: { + Description: 'AWS CloudFormation handler for "Custom::S3BucketNotifications" resources (@aws-cdk/aws-s3)', + Code: { ZipFile: `exports.handler = ${handler.toString()};` }, + Handler: 'index.handler', + Role: role.roleArn, + Runtime: 'nodejs8.10', + Timeout: 300, + } + }); + + this.functionArn = resource.getAtt('Arn'); + } +} + +// tslint:disable:no-console + +/** + * Lambda event handler for the custom resource. Bear in mind that we are going + * to .toString() this function and inline it as Lambda code. + * + * The function will issue a putBucketNotificationConfiguration request for the + * specified bucket. + */ +const handler = (event: any, context: any) => { + const s3 = new (require('aws-sdk').S3)(); + const https = require("https"); + const url = require("url"); + + log(JSON.stringify(event, undefined, 2)); + + const props = event.ResourceProperties; + + if (event.RequestType === 'Delete') { + props.NotificationConfiguration = { }; // this is how you clean out notifications + } + + const req = { + Bucket: props.BucketName, + NotificationConfiguration: props.NotificationConfiguration + }; + + return s3.putBucketNotificationConfiguration(req, (err: any, data: any) => { + log({ err, data }); + if (err) { + return submitResponse("FAILED", err.message + `\nMore information in CloudWatch Log Stream: ${context.logStreamName}`); + } else { + return submitResponse("SUCCESS"); + } + }); + + function log(obj: any) { + console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj); + } + + // tslint:disable-next-line:max-line-length + // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule + // to allow sending an error messge as a reason. + function submitResponse(responseStatus: string, reason?: string) { + const responseBody = JSON.stringify({ + Status: responseStatus, + Reason: reason || "See the details in CloudWatch Log Stream: " + context.logStreamName, + PhysicalResourceId: context.logStreamName, + StackId: event.StackId, + RequestId: event.RequestId, + LogicalResourceId: event.LogicalResourceId, + NoEcho: false, + }); + + log({ responseBody }); + + const parsedUrl = url.parse(event.ResponseURL); + const options = { + hostname: parsedUrl.hostname, + port: 443, + path: parsedUrl.path, + method: "PUT", + headers: { + "content-type": "", + "content-length": responseBody.length + } + }; + + const request = https.request(options, (r: any) => { + log({ statusCode: r.statusCode, statusMessage: r.statusMessage }); + context.done(); + }); + + request.on("error", (error: any) => { + log({ sendError: error }); + context.done(); + }); + + request.write(responseBody); + request.end(); + } +}; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts new file mode 100644 index 0000000000000..fb14744f32346 --- /dev/null +++ b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts @@ -0,0 +1,172 @@ +import cdk = require('@aws-cdk/cdk'); +import { Bucket, EventType, NotificationKeyFilter } from '../bucket'; +import { BucketNotificationDestinationType, IBucketNotificationDestination } from '../notification-dest'; +import { NotificationsResourceHandler } from './notifications-resource-handler'; + +interface NotificationsProps { + /** + * The bucket to manage notifications for. + * + * This cannot be a `BucketRef` because the bucket maintains the 1:1 + * relationship with this resource. + */ + bucket: Bucket; +} + +/** + * A custom CloudFormation resource that updates bucket notifications for a + * bucket. The reason we need it is because the AWS::S3::Bucket notification + * configuration is defined on the bucket itself, which makes it impossible to + * provision notifications at the same time as the target (since + * PutBucketNotifications validates the targets). + * + * Since only a single BucketNotifications resource is allowed for each Bucket, + * this construct is not exported in the public API of this module. Instead, it + * is created just-in-time by `s3.Bucket.onEvent`, so a 1:1 relationship is + * ensured. + * + * @see + * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket-notificationconfig.html + */ +export class BucketNotifications extends cdk.Construct { + private readonly lambdaNotifications = new Array(); + private readonly queueNotifications = new Array(); + private readonly topicNotifications = new Array(); + private customResourceCreated = false; + private readonly bucket: Bucket; + + constructor(parent: cdk.Construct, id: string, props: NotificationsProps) { + super(parent, id); + this.bucket = props.bucket; + } + + /** + * Adds a notification subscription for this bucket. + * If this is the first notification, a BucketNotification resource is added to the stack. + * + * @param event The type of event + * @param target The target construct + * @param filters A set of S3 key filters + */ + public addNotification(event: EventType, target: IBucketNotificationDestination, ...filters: NotificationKeyFilter[]) { + this.createResourceOnce(); + + // resolve target. this also provides an opportunity for the target to e.g. update + // policies to allow this notification to happen. + const targetProps = target.asBucketNotificationDestination(this.bucket); + const commonConfig: CommonConfiguration = { + Events: [ event ], + Filter: renderFilters(filters), + }; + + // based on the target type, add the the correct configurations array + switch (targetProps.type) { + case BucketNotificationDestinationType.Lambda: + this.lambdaNotifications.push({ ...commonConfig, LambdaFunctionArn: targetProps.arn }); + break; + + case BucketNotificationDestinationType.Queue: + this.queueNotifications.push({ ...commonConfig, QueueArn: targetProps.arn }); + break; + + case BucketNotificationDestinationType.Topic: + this.topicNotifications.push({ ...commonConfig, TopicArn: targetProps.arn }); + break; + + default: + throw new Error('Unsupported notification target type:' + BucketNotificationDestinationType[targetProps.type]); + } + } + + private renderNotificationConfiguration(): NotificationConfiguration { + return { + LambdaFunctionConfigurations: this.lambdaNotifications.length > 0 ? this.lambdaNotifications : undefined, + QueueConfigurations: this.queueNotifications.length > 0 ? this.queueNotifications : undefined, + TopicConfigurations: this.topicNotifications.length > 0 ? this.topicNotifications : undefined + }; + } + + /** + * Defines the bucket notifications resources in the stack only once. + * This is called lazily as we add notifications, so that if notifications are not added, + * there is no notifications resource. + */ + private createResourceOnce() { + if (this.customResourceCreated) { + return; + } + + const handlerArn = NotificationsResourceHandler.singleton(this); + new cdk.Resource(this, 'Resource', { + type: 'Custom::S3BucketNotifications', + properties: { + ServiceToken: handlerArn, + BucketName: this.bucket.bucketName, + NotificationConfiguration: new cdk.Token(() => this.renderNotificationConfiguration()) + } + }); + + this.customResourceCreated = true; + } +} + +function renderFilters(filters?: NotificationKeyFilter[]): Filter | undefined { + if (!filters || filters.length === 0) { + return undefined; + } + + const renderedRules = new Array(); + + for (const rule of filters) { + if (!rule.suffix && !rule.prefix) { + throw new Error('NotificationKeyFilter must specify `prefix` and/or `suffix`'); + } + + if (rule.suffix) { + renderedRules.push({ Name: 'suffix', Value: rule.suffix }); + } + + if (rule.prefix) { + renderedRules.push({ Name: 'prefix', Value: rule.prefix }); + } + } + + return { + Key: { + FilterRules: renderedRules + } + }; +} + +interface NotificationConfiguration { + LambdaFunctionConfigurations?: LambdaFunctionConfiguration[]; + QueueConfigurations?: QueueConfiguration[]; + TopicConfigurations?: TopicConfiguration[]; +} + +interface CommonConfiguration { + Id?: string; + Events: EventType[]; + Filter?: Filter +} + +interface LambdaFunctionConfiguration extends CommonConfiguration { + LambdaFunctionArn: cdk.Arn; +} + +interface QueueConfiguration extends CommonConfiguration { + QueueArn: cdk.Arn; +} + +interface TopicConfiguration extends CommonConfiguration { + TopicArn: cdk.Arn; +} + +interface FilterRule { + Name: 'prefix' | 'suffix'; + Value: string; +} + +interface Filter { + Key: { FilterRules: FilterRule[] } +} diff --git a/packages/@aws-cdk/aws-s3/test/integ.notifications.expected.json b/packages/@aws-cdk/aws-s3/test/integ.notifications.expected.json new file mode 100644 index 0000000000000..5a06b33722f90 --- /dev/null +++ b/packages/@aws-cdk/aws-s3/test/integ.notifications.expected.json @@ -0,0 +1,273 @@ +{ + "Resources": { + "Bucket83908E77": { + "Type": "AWS::S3::Bucket" + }, + "BucketNotifications8F2E257D": { + "Type": "Custom::S3BucketNotifications", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", + "Arn" + ] + }, + "BucketName": { + "Ref": "Bucket83908E77" + }, + "NotificationConfiguration": { + "TopicConfigurations": [ + { + "Events": [ + "s3:ObjectCreated:Put" + ], + "TopicArn": { + "Ref": "TopicBFC7AF6E" + } + }, + { + "Events": [ + "s3:ObjectRemoved:*" + ], + "Filter": { + "Key": { + "FilterRules": [ + { + "Name": "prefix", + "Value": "home/myusername/" + } + ] + } + }, + "TopicArn": { + "Ref": "Topic3DEAE47A7" + } + } + ] + } + } + }, + "TopicBFC7AF6E": { + "Type": "AWS::SNS::Topic" + }, + "TopicPolicy7C94FB28": { + "Type": "AWS::SNS::TopicPolicy", + "Properties": { + "Topics": [ + { + "Ref": "TopicBFC7AF6E" + } + ], + "PolicyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "s3.amazonaws.com" + }, + "Resource": { + "Ref": "TopicBFC7AF6E" + }, + "Sid": "sid0" + } + ], + "Version": "2012-10-17" + } + } + }, + "Topic3DEAE47A7": { + "Type": "AWS::SNS::Topic" + }, + "Topic3Policy9C00F2FA": { + "Type": "AWS::SNS::TopicPolicy", + "Properties": { + "Topics": [ + { + "Ref": "Topic3DEAE47A7" + } + ], + "PolicyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "s3.amazonaws.com" + }, + "Resource": { + "Ref": "Topic3DEAE47A7" + }, + "Sid": "sid0" + }, + { + "Action": "sns:Publish", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "Bucket25524B414", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "s3.amazonaws.com" + }, + "Resource": { + "Ref": "Topic3DEAE47A7" + }, + "Sid": "sid1" + } + ], + "Version": "2012-10-17" + } + } + }, + "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn", + ":", + { + "Ref": "AWS::Partition" + }, + ":", + "iam", + ":", + "", + ":", + "aws", + ":", + "policy", + "/", + "service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleDefaultPolicy2CF63D36": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutBucketNotification", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleDefaultPolicy2CF63D36", + "Roles": [ + { + "Ref": "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC" + } + ] + } + }, + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", + "Code": { + "ZipFile": "exports.handler = (event, context) => {\n const s3 = new (require('aws-sdk').S3)();\n const https = require(\"https\");\n const url = require(\"url\");\n log(JSON.stringify(event, undefined, 2));\n const props = event.ResourceProperties;\n if (event.RequestType === 'Delete') {\n props.NotificationConfiguration = {}; // this is how you clean out notifications\n }\n const req = {\n Bucket: props.BucketName,\n NotificationConfiguration: props.NotificationConfiguration\n };\n return s3.putBucketNotificationConfiguration(req, (err, data) => {\n log({ err, data });\n if (err) {\n return submitResponse(\"FAILED\", err.message + `\\nMore information in CloudWatch Log Stream: ${context.logStreamName}`);\n }\n else {\n return submitResponse(\"SUCCESS\");\n }\n });\n function log(obj) {\n console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj);\n }\n // tslint:disable-next-line:max-line-length\n // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule\n // to allow sending an error messge as a reason.\n function submitResponse(responseStatus, reason) {\n const responseBody = JSON.stringify({\n Status: responseStatus,\n Reason: reason || \"See the details in CloudWatch Log Stream: \" + context.logStreamName,\n PhysicalResourceId: context.logStreamName,\n StackId: event.StackId,\n RequestId: event.RequestId,\n LogicalResourceId: event.LogicalResourceId,\n NoEcho: false,\n });\n log({ responseBody });\n const parsedUrl = url.parse(event.ResponseURL);\n const options = {\n hostname: parsedUrl.hostname,\n port: 443,\n path: parsedUrl.path,\n method: \"PUT\",\n headers: {\n \"content-type\": \"\",\n \"content-length\": responseBody.length\n }\n };\n const request = https.request(options, (r) => {\n log({ statusCode: r.statusCode, statusMessage: r.statusMessage });\n context.done();\n });\n request.on(\"error\", (error) => {\n log({ sendError: error });\n context.done();\n });\n request.write(responseBody);\n request.end();\n }\n};" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Timeout": 300 + } + }, + "Bucket25524B414": { + "Type": "AWS::S3::Bucket" + }, + "Bucket2NotificationsD9BA2A77": { + "Type": "Custom::S3BucketNotifications", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", + "Arn" + ] + }, + "BucketName": { + "Ref": "Bucket25524B414" + }, + "NotificationConfiguration": { + "TopicConfigurations": [ + { + "Events": [ + "s3:ObjectRemoved:*" + ], + "Filter": { + "Key": { + "FilterRules": [ + { + "Name": "prefix", + "Value": "foo" + }, + { + "Name": "suffix", + "Value": "foo/bar" + } + ] + } + }, + "TopicArn": { + "Ref": "Topic3DEAE47A7" + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3/test/integ.notifications.ts b/packages/@aws-cdk/aws-s3/test/integ.notifications.ts new file mode 100644 index 0000000000000..c6e5977247361 --- /dev/null +++ b/packages/@aws-cdk/aws-s3/test/integ.notifications.ts @@ -0,0 +1,20 @@ +import cdk = require('@aws-cdk/cdk'); +import { Stack } from '@aws-cdk/cdk'; +import s3 = require('../lib'); +import { Topic } from './notification-dests'; + +const app = new cdk.App(process.argv); + +const stack = new Stack(app, 'test-3'); + +const bucket = new s3.Bucket(stack, 'Bucket'); +const topic = new Topic(stack, 'Topic'); +const topic3 = new Topic(stack, 'Topic3'); + +bucket.onEvent(s3.EventType.ObjectCreatedPut, topic); +bucket.onEvent(s3.EventType.ObjectRemoved, topic3, { prefix: 'home/myusername/' }); + +const bucket2 = new s3.Bucket(stack, 'Bucket2'); +bucket2.onObjectRemoved(topic3, { prefix: 'foo' }, { suffix: 'foo/bar' }); + +process.stdout.write(app.run()); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3/test/notification-dests.ts b/packages/@aws-cdk/aws-s3/test/notification-dests.ts new file mode 100644 index 0000000000000..afdf24035bc4c --- /dev/null +++ b/packages/@aws-cdk/aws-s3/test/notification-dests.ts @@ -0,0 +1,47 @@ +import cdk = require('@aws-cdk/cdk'); +import s3 = require('../lib'); + +/** + * Since we can't take a dependency on @aws-cdk/sns, this is a simple wrapper + * for AWS::SNS::Topic which implements IBucketNotificationDestination. + */ +export class Topic extends cdk.Construct implements s3.IBucketNotificationDestination { + public readonly topicArn: cdk.Arn; + private readonly policy = new cdk.PolicyDocument(); + private readonly notifyingBucketPaths = new Set(); + + constructor(parent: cdk.Construct, id: string) { + super(parent, id); + + const resource = new cdk.Resource(this, 'Resource', { type: 'AWS::SNS::Topic' }); + + new cdk.Resource(this, 'Policy', { + type: 'AWS::SNS::TopicPolicy', + properties: { + Topics: [ resource.ref ], + PolicyDocument: this.policy + } + }); + + this.topicArn = resource.ref; + } + + public asBucketNotificationDestination(bucket: s3.Bucket): s3.BucketNotificationDestinationProps { + + // add permission to each source bucket + if (!this.notifyingBucketPaths.has(bucket.path)) { + this.policy.addStatement(new cdk.PolicyStatement() + .describe(`sid${this.policy.statementCount}`) + .addServicePrincipal('s3.amazonaws.com') + .addAction('sns:Publish') + .addResource(this.topicArn) + .addCondition('ArnLike', { "aws:SourceArn": bucket.bucketArn })); + this.notifyingBucketPaths.add(bucket.path); + } + + return { + arn: this.topicArn, + type: s3.BucketNotificationDestinationType.Topic + }; + } +} diff --git a/packages/@aws-cdk/aws-s3/test/test.notifications.ts b/packages/@aws-cdk/aws-s3/test/test.notifications.ts new file mode 100644 index 0000000000000..adbd7e7fef925 --- /dev/null +++ b/packages/@aws-cdk/aws-s3/test/test.notifications.ts @@ -0,0 +1,272 @@ +import { expect, haveResource } from '@aws-cdk/assert'; +import cdk = require('@aws-cdk/cdk'); +import { Test } from 'nodeunit'; +import s3 = require('../lib'); +import { Topic } from './notification-dests'; + +// tslint:disable:object-literal-key-quotes +// tslint:disable:max-line-length + +export = { + 'bucket without notifications'(test: Test) { + const stack = new cdk.Stack(); + + new s3.Bucket(stack, 'MyBucket'); + + expect(stack).toMatch({ + "Resources": { + "MyBucketF68F3FF0": { + "Type": "AWS::S3::Bucket" + } + } + }); + + test.done(); + }, + + 'when notification are added, a custom resource is provisioned + a lambda handler for it'(test: Test) { + const stack = new cdk.Stack(); + + const bucket = new s3.Bucket(stack, 'MyBucket'); + + const topic = new Topic(stack, 'MyTopic'); + + bucket.onEvent(s3.EventType.ObjectCreated, topic); + + expect(stack).to(haveResource('AWS::S3::Bucket')); + expect(stack).to(haveResource('AWS::Lambda::Function', { Description: 'AWS CloudFormation handler for "Custom::S3BucketNotifications" resources (@aws-cdk/aws-s3)' })); + expect(stack).to(haveResource('Custom::S3BucketNotifications')); + + test.done(); + }, + + 'bucketNotificationTarget is not called during synthesis'(test: Test) { + const stack = new cdk.Stack(); + + // notice the order here - topic is defined before bucket + // but this shouldn't impact the fact that the topic policy includes + // the bucket information + const topic = new Topic(stack, 'Topic'); + const bucket = new s3.Bucket(stack, 'MyBucket'); + + bucket.onObjectCreated(topic); + + expect(stack).to(haveResource('AWS::SNS::TopicPolicy', { + "Topics": [ + { + "Ref": "TopicBFC7AF6E" + } + ], + "PolicyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "s3.amazonaws.com" + }, + "Resource": { + "Ref": "TopicBFC7AF6E" + }, + "Sid": "sid0" + } + ], + "Version": "2012-10-17" + } + })); + + test.done(); + }, + + 'subscription types'(test: Test) { + const stack = new cdk.Stack(); + + const bucket = new s3.Bucket(stack, 'TestBucket'); + + const queueTarget: s3.IBucketNotificationDestination = { + asBucketNotificationDestination: _ => ({ + type: s3.BucketNotificationDestinationType.Queue, + arn: new cdk.Arn('arn:aws:sqs:...') + }) + }; + + const lambdaTarget: s3.IBucketNotificationDestination = { + asBucketNotificationDestination: _ => ({ + type: s3.BucketNotificationDestinationType.Lambda, + arn: new cdk.Arn('arn:aws:lambda:...') + }) + }; + + const topicTarget: s3.IBucketNotificationDestination = { + asBucketNotificationDestination: _ => ({ + type: s3.BucketNotificationDestinationType.Topic, + arn: new cdk.Arn('arn:aws:sns:...') + }) + }; + + bucket.onEvent(s3.EventType.ObjectCreated, queueTarget); + bucket.onEvent(s3.EventType.ObjectCreated, lambdaTarget); + bucket.onObjectRemoved(topicTarget, { prefix: 'prefix' }); + + expect(stack).to(haveResource('Custom::S3BucketNotifications', { + "ServiceToken": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", + "Arn" + ] + }, + "BucketName": { + "Ref": "TestBucket560B80BC" + }, + "NotificationConfiguration": { + "LambdaFunctionConfigurations": [ + { + "Events": [ + "s3:ObjectCreated:*" + ], + "LambdaFunctionArn": "arn:aws:lambda:..." + } + ], + "QueueConfigurations": [ + { + "Events": [ + "s3:ObjectCreated:*" + ], + "QueueArn": "arn:aws:sqs:..." + } + ], + "TopicConfigurations": [ + { + "Events": [ + "s3:ObjectRemoved:*" + ], + "TopicArn": "arn:aws:sns:...", + "Filter": { + "Key": { + "FilterRules": [ + { + "Name": "prefix", + "Value": "prefix" + } + ] + } + } + } + ] + } + })); + + test.done(); + }, + + 'multiple subscriptions of the same type'(test: Test) { + const stack = new cdk.Stack(); + + const bucket = new s3.Bucket(stack, 'TestBucket'); + + bucket.onEvent(s3.EventType.ObjectRemovedDelete, { + asBucketNotificationDestination: _ => ({ + type: s3.BucketNotificationDestinationType.Queue, + arn: new cdk.Arn('arn:aws:sqs:...:queue1') + }) + }); + + bucket.onEvent(s3.EventType.ObjectRemovedDelete, { + asBucketNotificationDestination: _ => ({ + type: s3.BucketNotificationDestinationType.Queue, + arn: new cdk.Arn('arn:aws:sqs:...:queue2') + }) + }); + + expect(stack).to(haveResource('Custom::S3BucketNotifications', { + "ServiceToken": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", + "Arn" + ] + }, + "BucketName": { + "Ref": "TestBucket560B80BC" + }, + "NotificationConfiguration": { + "QueueConfigurations": [ + { + "Events": [ + "s3:ObjectRemoved:Delete" + ], + "QueueArn": "arn:aws:sqs:...:queue1" + }, + { + "Events": [ + "s3:ObjectRemoved:Delete" + ], + "QueueArn": "arn:aws:sqs:...:queue2" + } + ] + } + })); + + test.done(); + }, + + 'prefix/suffix filters'(test: Test) { + const stack = new cdk.Stack(); + + const bucket = new s3.Bucket(stack, 'TestBucket'); + + const bucketNotificationTarget = { + type: s3.BucketNotificationDestinationType.Queue, + arn: new cdk.Arn('arn:aws:sqs:...') + }; + + bucket.onEvent(s3.EventType.ObjectRemovedDelete, { asBucketNotificationDestination: _ => bucketNotificationTarget }, { prefix: 'images/', suffix: '.jpg' }); + + expect(stack).to(haveResource('Custom::S3BucketNotifications', { + "ServiceToken": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", + "Arn" + ] + }, + "BucketName": { + "Ref": "TestBucket560B80BC" + }, + "NotificationConfiguration": { + "QueueConfigurations": [ + { + "Events": [ + "s3:ObjectRemoved:Delete" + ], + "Filter": { + "Key": { + "FilterRules": [ + { + "Name": "suffix", + "Value": ".jpg" + }, + { + "Name": "prefix", + "Value": "images/" + } + ] + } + }, + "QueueArn": "arn:aws:sqs:..." + } + ] + } + })); + + test.done(); + } +}; From dec319bb3693730083c7c4be5df0ca6e7183d241 Mon Sep 17 00:00:00 2001 From: Doug Date: Mon, 13 Aug 2018 10:14:55 -0700 Subject: [PATCH 07/33] Update README.md Explicitly called out upgrading --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 85d012a7f8427..28c0f8933ccb8 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ on AWS. * For a detailed walkthrough, see [Getting Started] in the AWS CDK [User Guide] * See [Manual Installation](./MANUAL_INSTALLATION.md) for installing the CDK from a signed .zip file -Install the latest released version of the [AWS CDK Toolkit] from npm (requires [Node.js ≥ 8.11.x](https://nodejs.org/en/download)): +Install the [AWS CDK Toolkit] or update your installed version of the |cdk| to the latest version from npm (requires [Node.js ≥ 8.11.x](https://nodejs.org/en/download)): ```bash $ npm i -g aws-cdk From 0efe25b0b2eb2ef75a7f4230fd690ebcc50256ad Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Tue, 14 Aug 2018 10:36:02 +0300 Subject: [PATCH 08/33] feat(cdk): construct.uniqueId and relax construct id constraints (#556) Adds a property `uniqueId` to Construct which returns an alphanumeric 255-length-limited tree-unique identity for a construct. Relax constraints for construct id (previously known as "name") to only restrict the usage of the path separator. Otherwise, all characters are allowed. This will allow using the construct id for a wider range of purposes, but since logical IDs (and uniqueId now) are alpha-safe, it's okay. Deprecate `construct.name` in favor of `construct.id`. --- packages/@aws-cdk/cdk/lib/app.ts | 4 +- .../cdk/lib/cloudformation/logical-id.ts | 67 +-------------- .../@aws-cdk/cdk/lib/cloudformation/output.ts | 2 +- .../@aws-cdk/cdk/lib/cloudformation/stack.ts | 2 +- packages/@aws-cdk/cdk/lib/core/construct.ts | 68 ++++++++------- packages/@aws-cdk/cdk/lib/core/jsx.ts | 8 +- packages/@aws-cdk/cdk/lib/util/uniqueid.ts | 85 +++++++++++++++++++ .../test/cloudformation/test.logical-id.ts | 11 +++ .../@aws-cdk/cdk/test/core/test.construct.ts | 51 +++++++---- packages/@aws-cdk/cdk/test/core/test.jsx.tsx | 21 ++--- packages/@aws-cdk/cdk/test/test.app.ts | 2 +- packages/@aws-cdk/cdk/test/test.context.ts | 2 +- 12 files changed, 190 insertions(+), 133 deletions(-) create mode 100644 packages/@aws-cdk/cdk/lib/util/uniqueid.ts diff --git a/packages/@aws-cdk/cdk/lib/app.ts b/packages/@aws-cdk/cdk/lib/app.ts index e79938d4478e0..6d157783aef34 100644 --- a/packages/@aws-cdk/cdk/lib/app.ts +++ b/packages/@aws-cdk/cdk/lib/app.ts @@ -47,7 +47,7 @@ export class App extends Root { throw new Error(`The child ${child.toString()} of Program must be a Stack`); } - out[child.name] = child as Stack; + out[child.id] = child as Stack; } return out; } @@ -113,7 +113,7 @@ export class App extends Root { } return { - name: stack.name, + name: stack.id, environment, missing: Object.keys(stack.missingContext).length ? stack.missingContext : undefined, template: stack.toCloudFormation(), diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/logical-id.ts b/packages/@aws-cdk/cdk/lib/cloudformation/logical-id.ts index 4bc66fd13b4f9..5f89093795981 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/logical-id.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/logical-id.ts @@ -1,11 +1,7 @@ +import { makeUniqueId } from '../util/uniqueid'; import { StackElement } from "./stack"; -// tslint:disable-next-line:no-var-requires -const md5 = require('../util/md5'); - const PATH_SEP = '/'; -const HASH_LEN = 8; -const MAX_HUMAN_LEN = 240; // max ID len is 255 /** * Interface for classes that implementation logical ID assignment strategies @@ -55,42 +51,10 @@ export interface IAddressingScheme { */ export class HashedAddressingScheme implements IAddressingScheme { public allocateAddress(addressComponents: string[]): string { - addressComponents = addressComponents.filter(x => x !== HIDDEN_ID); - - if (addressComponents.length === 0) { - throw new Error('Construct has empty Logical ID'); - } - - // top-level resources will simply use the `name` as-is in order to support - // transparent migration of cloudformation templates to the CDK without the - // need to rename all resources. - if (addressComponents.length === 1) { - return addressComponents[0]; - } - - const hash = pathHash(addressComponents); - const human = removeDupes(addressComponents) - .filter(x => x !== HIDDEN_FROM_HUMAN_ID) - .join('') - .slice(0, MAX_HUMAN_LEN); - - return human + hash; + return makeUniqueId(addressComponents); } } -/** - * Resources with this ID are hidden from humans - * - * They do not appear in the human-readable part of the logical ID, - * but they are included in the hash calculation. - */ -const HIDDEN_FROM_HUMAN_ID = 'Resource'; - -/** - * Resources with this ID are complete hidden from the logical ID calculation. - */ -const HIDDEN_ID = 'Default'; - /** * Class that keeps track of the logical IDs that are assigned to resources * @@ -176,15 +140,6 @@ export class LogicalIDs { } } -/** - * Take a hash of the given path. - * - * The hash is limited in size. - */ -function pathHash(path: string[]): string { - return md5(path.join(PATH_SEP)).slice(0, HASH_LEN).toUpperCase(); -} - const VALID_LOGICALID_REGEX = /^[A-Za-z][A-Za-z0-9]{1,254}$/; /** @@ -195,21 +150,3 @@ function validateLogicalId(logicalId: string) { throw new Error(`Logical ID must adhere to the regular expression: ${VALID_LOGICALID_REGEX.toString()}, got '${logicalId}'`); } } - -/** - * Remove duplicate "terms" from the path list - * - * If a component name is completely the same as the suffix of - * the previous component name, we get rid of it. - */ -function removeDupes(path: string[]): string[] { - const ret = new Array(); - - for (const component of path) { - if (ret.length === 0 || !ret[ret.length - 1].endsWith(component)) { - ret.push(component); - } - } - - return ret; -} diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/output.ts b/packages/@aws-cdk/cdk/lib/cloudformation/output.ts index f4135f9b3e264..c3e8e298c10c3 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/output.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/output.ts @@ -91,7 +91,7 @@ export class Output extends StackElement { this.export = props.export; } else if (!props.disableExport) { // prefix export name with stack name since exports are global within account + region. - const stackName = Stack.find(this).name; + const stackName = Stack.find(this).id; this.export = stackName ? stackName + ':' : ''; this.export += this.logicalId; } diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts b/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts index 81ee55645eb2a..9afe7265bdec4 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts @@ -332,7 +332,7 @@ export abstract class StackElement extends Construct implements IDependable { * Return the path with respect to the stack */ public get stackPath(): string { - return this.ancestors(this.stack).map(c => c.name).join(PATH_SEP); + return this.ancestors(this.stack).map(c => c.id).join(PATH_SEP); } public get dependencyElements(): IDependable[] { diff --git a/packages/@aws-cdk/cdk/lib/core/construct.ts b/packages/@aws-cdk/cdk/lib/core/construct.ts index f07790d4c4c91..3b52c7092b7d1 100644 --- a/packages/@aws-cdk/cdk/lib/core/construct.ts +++ b/packages/@aws-cdk/cdk/lib/core/construct.ts @@ -1,4 +1,5 @@ import cxapi = require('@aws-cdk/cx-api'); +import { makeUniqueId } from '../util/uniqueid'; export const PATH_SEP = '/'; /** @@ -6,18 +7,34 @@ export const PATH_SEP = '/'; * When a construct is created, it is always added as a child */ export class Construct { - private static readonly VALID_NAME_REGEX = /^[A-Za-z][A-Za-z0-9]*$/; - /** * Returns the parent of this node or undefined if this is a root node. */ public readonly parent?: Construct; /** - * The name of this construct + * @deprecated use `id` */ public readonly name: string; + /** + * The subtree-local id of the construct. + * This id is unique within the subtree. To obtain a tree-unique id, use `uniqueId`. + */ + public readonly id: string; + + /** + * The full path of this construct in the tree. + * Components are separated by '/'. + */ + public readonly path: string; + + /** + * A tree-unique alpha-numeric identifier for this construct. + * Includes all components of the tree. + */ + public readonly uniqueId: string; + /** * List of children and their names */ @@ -37,28 +54,33 @@ export class Construct { * @param parent The parent construct * @param props Properties for this construct */ - constructor(parent: Construct, name: string) { - this.name = name; + constructor(parent: Construct, id: string) { + this.id = id; + this.name = id; // legacy this.parent = parent; // We say that parent is required, but some root constructs bypass the type checks and // actually pass in 'undefined'. if (parent != null) { - if (name === '') { + if (id === '') { throw new Error('Only root constructs may have an empty name'); } // Has side effect so must be very last thing in constructor - parent.addChild(this, this.name); + parent.addChild(this, this.id); } else { // This is a root construct. - this.name = name; + this.id = id; } // Validate the name we ended up with - if (this.name !== '') { - this._validateName(this.name); + if (this.id !== '') { + this._validateName(this.id); } + + const components = this.rootPath().map(c => c.id); + this.path = components.join(PATH_SEP); + this.uniqueId = components.length > 0 ? makeUniqueId(components) : ''; } /** @@ -77,7 +99,7 @@ export class Construct { for (let i = 0; i < depth; ++i) { out += ' '; } - const name = this.name || ''; + const name = this.id || ''; out += `${this.typename}${name.length > 0 ? ' [' + name + ']' : ''}\n`; for (const child of this.children) { out += child.toTreeString(depth + 1); @@ -137,7 +159,7 @@ export class Construct { */ public setContext(key: string, value: any) { if (this.children.length > 0) { - const names = this.children.map(c => c.name); + const names = this.children.map(c => c.id); throw new Error('Cannot set context after children have been added: ' + names.join(',')); } this.context[key] = value; @@ -173,15 +195,6 @@ export class Construct { return value; } - /** - * Returns the path of all constructs from root to this construct, in string form. - * - * @returns /-separated path of this Construct. - */ - public get path(): string { - return this.rootPath().map(c => c.name).join(PATH_SEP); - } - /** * An array of metadata objects associated with this construct. * This can be used, for example, to implement support for deprecation notices, source mapping, etc. @@ -281,17 +294,12 @@ export class Construct { } /** - * Validate that the name of the construct is a legal identifier - * - * At the moment, we restrict these to valid CloudFormation identifiers. - * - * Protected so it can be overridden by subclasses. Starts with _ to hide the virtual function from JSII, - * because we don't want this validation to involve asynchrony. This restricts it to only - * be overridable in (Type|Java)Script, but that suffices for now. + * Validate that the name of the construct is a legal identifier. + * Construct names can be any characters besides the path separator. */ protected _validateName(name: string) { - if (!Construct.VALID_NAME_REGEX.test(name)) { - throw new Error(`Name must adhere to the regular expression: ${Construct.VALID_NAME_REGEX.toString()}, got '${name}'`); + if (name.indexOf(PATH_SEP) !== -1) { + throw new Error(`Construct names cannot include '${PATH_SEP}': ${name}`); } } diff --git a/packages/@aws-cdk/cdk/lib/core/jsx.ts b/packages/@aws-cdk/cdk/lib/core/jsx.ts index 9c14f2452acbf..3d22e33cb72fe 100644 --- a/packages/@aws-cdk/cdk/lib/core/jsx.ts +++ b/packages/@aws-cdk/cdk/lib/core/jsx.ts @@ -37,16 +37,16 @@ export namespace jsx { * @returns A Construct object */ export function construct(tree: any, parent?: Construct): Construct { - const name = (tree.props && tree.props.name) || ''; - const root = new tree.type(parent, name, tree.props); // create root + const id = (tree.props && tree.props.id) || ''; + const root = new tree.type(parent, id, tree.props); // create root createChildren(root, tree.children); return root; } function createChildren(parent: Construct, children: any[]) { for (const child of children) { - const name = (child.props && child.props.name) || ''; - const childObj = new child.type(parent, name, child.props); + const id = (child.props && child.props.id) || ''; + const childObj = new child.type(parent, id, child.props); createChildren(childObj, child.children); } } diff --git a/packages/@aws-cdk/cdk/lib/util/uniqueid.ts b/packages/@aws-cdk/cdk/lib/util/uniqueid.ts new file mode 100644 index 0000000000000..6c7e27a87ebf9 --- /dev/null +++ b/packages/@aws-cdk/cdk/lib/util/uniqueid.ts @@ -0,0 +1,85 @@ +// tslint:disable-next-line:no-var-requires +const md5 = require('./md5'); + +/** + * Resources with this ID are hidden from humans + * + * They do not appear in the human-readable part of the logical ID, + * but they are included in the hash calculation. + */ +const HIDDEN_FROM_HUMAN_ID = 'Resource'; + +/** + * Resources with this ID are complete hidden from the logical ID calculation. + */ +const HIDDEN_ID = 'Default'; + +const PATH_SEP = '/'; + +const HASH_LEN = 8; +const MAX_HUMAN_LEN = 240; // max ID len is 255 + +/** + * Given a set of named path components, returns a unique alpha-numeric identifier + * with a maximum length of 255. This is done by calculating a hash on the full path + * and using it as a suffix of a length-limited "human" rendition of the path components. + * + * @param components The path components + */ +export function makeUniqueId(components: string[]) { + components = components.filter(x => x !== HIDDEN_ID); + + if (components.length === 0) { + throw new Error('Unable to calculate a unique ID for an empty path'); + } + + // top-level resources will simply use the `name` as-is in order to support + // transparent migration of cloudformation templates to the CDK without the + // need to rename all resources. + if (components.length === 1) { + return components[0]; + } + + const hash = pathHash(components); + const human = removeDupes(components) + .map(removeNonAlpha) + .filter(x => x !== HIDDEN_FROM_HUMAN_ID) + .join('') + .slice(0, MAX_HUMAN_LEN); + + return human + hash; +} + +/** + * Take a hash of the given path. + * + * The hash is limited in size. + */ +function pathHash(path: string[]): string { + return md5(path.join(PATH_SEP)).slice(0, HASH_LEN).toUpperCase(); +} + +/** + * Removes all non-alphanumeric characters in a string. + */ +function removeNonAlpha(s: string) { + return s.replace(/[^A-Za-z0-9]/g, ''); +} + +/** + * Remove duplicate "terms" from the path list + * + * If a component name is completely the same as the suffix of + * the previous component name, we get rid of it. + */ +function removeDupes(path: string[]): string[] { + const ret = new Array(); + + for (const component of path) { + if (ret.length === 0 || !ret[ret.length - 1].endsWith(component)) { + ret.push(component); + } + } + + return ret; +} diff --git a/packages/@aws-cdk/cdk/test/cloudformation/test.logical-id.ts b/packages/@aws-cdk/cdk/test/cloudformation/test.logical-id.ts index 7f8348b71db68..da8bda26f7672 100644 --- a/packages/@aws-cdk/cdk/test/cloudformation/test.logical-id.ts +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.logical-id.ts @@ -131,6 +131,17 @@ const uniqueTests = { // THEN: same ID, same object test.equal(theId1, theId2); + test.done(); + }, + + 'non-alphanumeric characters are removed from the human part of the logical ID'(test: Test) { + const scheme = new HashedAddressingScheme(); + const val1 = scheme.allocateAddress([ 'Foo-bar', 'B00m', 'Hello_World', '&&Horray Horray.' ]); + const val2 = scheme.allocateAddress([ 'Foobar', 'B00m', 'HelloWorld', 'HorrayHorray' ]); + + // same human part, different hash + test.deepEqual(val1, 'FoobarB00mHelloWorldHorrayHorray640E99FB'); + test.deepEqual(val2, 'FoobarB00mHelloWorldHorrayHorray744334FD'); test.done(); } }; diff --git a/packages/@aws-cdk/cdk/test/core/test.construct.ts b/packages/@aws-cdk/cdk/test/core/test.construct.ts index 0036e81520532..70d4c80577d59 100644 --- a/packages/@aws-cdk/cdk/test/core/test.construct.ts +++ b/packages/@aws-cdk/cdk/test/core/test.construct.ts @@ -8,7 +8,7 @@ import { Construct, Root } from '../../lib'; export = { 'the "Root" construct is a special construct which can be used as the root of the tree'(test: Test) { const root = new Root(); - test.equal(root.name, '', 'if not specified, name of a root construct is an empty string'); + test.equal(root.id, '', 'if not specified, name of a root construct is an empty string'); test.ok(!root.parent, 'no parent'); test.equal(root.children.length, 0, 'a construct is created without children'); // no children test.done(); @@ -23,32 +23,47 @@ export = { 'construct.name returns the name of the construct'(test: Test) { const t = createTree(); - test.equal(t.child1.name, 'Child1'); - test.equal(t.child2.name, 'Child2'); - test.equal(t.child1_1.name, 'Child11'); - test.equal(t.child1_2.name, 'Child12'); - test.equal(t.child1_1_1.name, 'Child111'); - test.equal(t.child2_1.name, 'Child21'); + test.equal(t.child1.id, 'Child1'); + test.equal(t.child2.id, 'Child2'); + test.equal(t.child1_1.id, 'Child11'); + test.equal(t.child1_2.id, 'Child12'); + test.equal(t.child1_1_1.id, 'Child111'); + test.equal(t.child2_1.id, 'Child21'); test.done(); }, - 'construct name can only be alpha-numeric at least one character long'(test: Test) { + 'construct id can use any character except the path separator'(test: Test) { const root = new Root(); new Construct(root, 'valid'); new Construct(root, 'ValiD'); new Construct(root, 'Va123lid'); new Construct(root, 'v'); - test.throws(() => new Construct(root, ' invalid' ), Error, 'no spaces before'); - test.throws(() => new Construct(root, 'invalid ' ), Error, 'no spaces after'); - test.throws(() => new Construct(root, '123invalid' ), Error, 'name can\'t begin with a number'); - test.throws(() => new Construct(root, 'in valid' ), Error, 'spaces are not allowed'); - test.throws(() => new Construct(root, 'in_Valid' ), Error, 'underscores are not allowed'); - test.throws(() => new Construct(root, 'in-Valid' ), Error, 'hyphens are not allowed'); + new Construct(root, ' invalid' ); + new Construct(root, 'invalid ' ); + new Construct(root, '123invalid' ); + new Construct(root, 'in valid' ); + new Construct(root, 'in_Valid' ); + new Construct(root, 'in-Valid' ); + new Construct(root, 'in\\Valid' ); + new Construct(root, 'in.Valid' ); + test.throws(() => new Construct(root, 'in/Valid' ), Error, 'backslashes are not allowed'); - test.throws(() => new Construct(root, 'in\\Valid' ), Error, 'slashes are not allowed'); - test.throws(() => new Construct(root, 'in.Valid' ), Error, 'periods are not allowed'); + test.done(); + }, + + 'construct.uniqueId returns a tree-unique alphanumeric id of this construct'(test: Test) { + const root = new Root(); + + const child1 = new Construct(root, 'This is the first child'); + const child2 = new Construct(child1, 'Second level'); + const c1 = new Construct(child2, 'My construct'); + const c2 = new Construct(child1, 'My construct'); + test.deepEqual(c1.path, 'This is the first child/Second level/My construct'); + test.deepEqual(c2.path, 'This is the first child/My construct'); + test.deepEqual(c1.uniqueId, 'ThisisthefirstchildSecondlevelMyconstruct202131E0'); + test.deepEqual(c2.uniqueId, 'ThisisthefirstchildMyconstruct8C288DF9'); test.done(); }, @@ -64,7 +79,7 @@ export = { 'construct.findChild(name) can be used to retrieve a child from a parent'(test: Test) { const root = new Root(); const child = new Construct(root, 'Contruct'); - test.strictEqual(root.tryFindChild(child.name), child, 'findChild(name) can be used to retrieve the child from a parent'); + test.strictEqual(root.tryFindChild(child.id), child, 'findChild(name) can be used to retrieve the child from a parent'); test.ok(!root.tryFindChild('NotFound'), 'findChild(name) returns undefined if the child is not found'); test.done(); }, @@ -72,7 +87,7 @@ export = { 'construct.getChild(name) can be used to retrieve a child from a parent'(test: Test) { const root = new Root(); const child = new Construct(root, 'Contruct'); - test.strictEqual(root.findChild(child.name), child, 'getChild(name) can be used to retrieve the child from a parent'); + test.strictEqual(root.findChild(child.id), child, 'getChild(name) can be used to retrieve the child from a parent'); test.throws(() => { root.findChild('NotFound'); }, '', 'getChild(name) returns undefined if the child is not found'); diff --git a/packages/@aws-cdk/cdk/test/core/test.jsx.tsx b/packages/@aws-cdk/cdk/test/core/test.jsx.tsx index 29b34e3819540..3ba98f8f63092 100644 --- a/packages/@aws-cdk/cdk/test/core/test.jsx.tsx +++ b/packages/@aws-cdk/cdk/test/core/test.jsx.tsx @@ -4,11 +4,11 @@ import { Construct, jsx, Root } from '../../lib'; export = { 'jsx can be used to create "trees" of constructs'(test: Test) { const tree = - - - + + + - + ; const root = jsx.construct(tree); @@ -21,7 +21,7 @@ export = { }, 'jsx.construct(tree) will actually create the object'(test: Test) { - const my = jsx.construct() as MyConstruct; + const my = jsx.construct() as MyConstruct; test.equal(my.calculate(), 'prop1=hey, id=foo'); test.done(); }, @@ -29,9 +29,9 @@ export = { 'jsx.construct(tree, parent) can be used to add a JSX tree into an existing construct tree'(test: Test) { const root = new Root(); - jsx.construct(, root); + jsx.construct(, root); - test.equal(root.findChild('child').name, 'child'); + test.equal(root.findChild('child').id, 'child'); test.done(); } }; @@ -57,14 +57,15 @@ class MyConstruct extends Construct { /** * Constructor will always receive `props` as the 3rd argument with properties passed via JSX. */ - constructor(parent: Construct, name: string, props: MyConstructProps) { - super(parent, name); + constructor(parent: Construct, id: string, props: MyConstructProps) { + super(parent, id); this.str = 'prop1=' + props.prop1; if (props.prop2) { this.str += ', prop2=' + props.prop2.toString(); } - this.str += ', id=' + this.name; + this.str += ', id=' + this.id; + this.foo = 123; } public calculate() { diff --git a/packages/@aws-cdk/cdk/test/test.app.ts b/packages/@aws-cdk/cdk/test/test.app.ts index 65eabdd056b08..2e2f264257cde 100644 --- a/packages/@aws-cdk/cdk/test/test.app.ts +++ b/packages/@aws-cdk/cdk/test/test.app.ts @@ -222,7 +222,7 @@ export = { class Child extends Construct { public validate() { - return [ `Error from ${this.name}` ]; + return [ `Error from ${this.id}` ]; } } diff --git a/packages/@aws-cdk/cdk/test/test.context.ts b/packages/@aws-cdk/cdk/test/test.context.ts index 558f324c06c51..de59851775862 100644 --- a/packages/@aws-cdk/cdk/test/test.context.ts +++ b/packages/@aws-cdk/cdk/test/test.context.ts @@ -62,7 +62,7 @@ export = { test.deepEqual(new AvailabilityZoneProvider(stack).availabilityZones, [ 'dummy1a', 'dummy1b', 'dummy1c' ]); test.deepEqual(new SSMParameterProvider(child).getString('foo'), 'dummy'); - const output = app.synthesizeStack(stack.name); + const output = app.synthesizeStack(stack.id); const azError: MetadataEntry | undefined = output.metadata['/test-stack'].find(x => x.type === cxapi.ERROR_METADATA_KEY); const ssmError: MetadataEntry | undefined = output.metadata['/test-stack/ChildConstruct'].find(x => x.type === cxapi.ERROR_METADATA_KEY); From 327f5e12d288c5d9099c4beb2fe16e1c19acd055 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Tue, 14 Aug 2018 10:57:26 +0300 Subject: [PATCH 09/33] fix(aws-lambda): CloudWatch event rule permissions (#558) Lambda permissions granted when it was added as an event rule target did not include "SourceArn" as required. This allowed any event rule to trigger the function, and also did not show as a trigger in the AWS Lambda console. Added a integration test to verify. BREAKING CHANGE To fix this, we needed to modify `IEventRuleTarget` to pass the ARN of the rule and a unique ID to the registered target in order to allow it to specify the Source ARN. This required fixing all existing event rule targets (which, so far would return a role to be assumed by CWE, so the source ARN was not required). Fixes #555 --- .../@aws-cdk/aws-codebuild/lib/project.ts | 2 +- .../@aws-cdk/aws-codepipeline/lib/pipeline.ts | 2 +- packages/@aws-cdk/aws-events/lib/rule.ts | 8 +- packages/@aws-cdk/aws-events/lib/target.ts | 7 +- .../@aws-cdk/aws-events/test/test.rule.ts | 48 +++++-- .../@aws-cdk/aws-lambda/lib/lambda-ref.ts | 18 +-- .../test/integ.events.expected.json | 132 ++++++++++++++++++ .../@aws-cdk/aws-lambda/test/integ.events.ts | 27 ++++ .../test/integ.inline.expected.json | 2 +- .../@aws-cdk/aws-lambda/test/test.lambda.ts | 19 ++- packages/@aws-cdk/aws-sns/lib/topic-ref.ts | 4 +- 11 files changed, 235 insertions(+), 34 deletions(-) create mode 100644 packages/@aws-cdk/aws-lambda/test/integ.events.expected.json create mode 100644 packages/@aws-cdk/aws-lambda/test/integ.events.ts diff --git a/packages/@aws-cdk/aws-codebuild/lib/project.ts b/packages/@aws-cdk/aws-codebuild/lib/project.ts index aefa0c5699918..1fc3925b3c26b 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -254,7 +254,7 @@ export abstract class ProjectRef extends cdk.Construct implements events.IEventR /** * Allows using build projects as event rule targets. */ - public get eventRuleTarget(): events.EventRuleTargetProps { + public asEventRuleTarget(_ruleArn: events.RuleArn, _ruleId: string): events.EventRuleTargetProps { if (!this.eventsRole) { this.eventsRole = new iam.Role(this, 'EventsRole', { assumedBy: new cdk.ServicePrincipal('events.amazonaws.com') diff --git a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts index fe226459edb8e..7b72cbfe7ec01 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts @@ -148,7 +148,7 @@ export class Pipeline extends cdk.Construct implements events.IEventRuleTarget { * rule.addTarget(pipeline); * */ - public get eventRuleTarget(): events.EventRuleTargetProps { + public asEventRuleTarget(_ruleArn: events.RuleArn, _ruleId: string): events.EventRuleTargetProps { // the first time the event rule target is retrieved, we define an IAM // role assumable by the CloudWatch events service which is allowed to // start the execution of this pipeline. no need to define more than one diff --git a/packages/@aws-cdk/aws-events/lib/rule.ts b/packages/@aws-cdk/aws-events/lib/rule.ts index 1bb895b174bb1..14e1fa20f241a 100644 --- a/packages/@aws-cdk/aws-events/lib/rule.ts +++ b/packages/@aws-cdk/aws-events/lib/rule.ts @@ -100,13 +100,15 @@ export class EventRule extends EventRuleRef { public addTarget(target?: IEventRuleTarget, inputOptions?: TargetInputTemplate) { if (!target) { return; } + const targetProps = target.asEventRuleTarget(this.ruleArn, this.uniqueId); + // check if a target with this ID already exists - if (this.targets.find(t => t.id === target.eventRuleTarget.id)) { - throw new Error('Duplicate event rule target with ID: ' + target.eventRuleTarget.id); + if (this.targets.find(t => t.id === targetProps.id)) { + throw new Error('Duplicate event rule target with ID: ' + targetProps.id); } this.targets.push({ - ...target.eventRuleTarget, + ...targetProps, inputTransformer: renderTransformer(), }); diff --git a/packages/@aws-cdk/aws-events/lib/target.ts b/packages/@aws-cdk/aws-events/lib/target.ts index b5ea5fde3b1a8..557abc90a651d 100644 --- a/packages/@aws-cdk/aws-events/lib/target.ts +++ b/packages/@aws-cdk/aws-events/lib/target.ts @@ -1,6 +1,6 @@ import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); -import { cloudformation } from './events.generated'; +import { cloudformation, RuleArn } from './events.generated'; export interface EventRuleTargetProps { /** @@ -50,6 +50,9 @@ export interface IEventRuleTarget { /** * Returns the rule target specification. * NOTE: Do not use the various `inputXxx` options. They can be set in a call to `addTarget`. + * + * @param ruleArn The ARN of the CloudWatch Event Rule that would trigger this target. + * @param ruleUniqueId A unique ID for this rule. Can be used to implement idempotency. */ - readonly eventRuleTarget: EventRuleTargetProps; + asEventRuleTarget(ruleArn: RuleArn, ruleUniqueId: string): EventRuleTargetProps; } diff --git a/packages/@aws-cdk/aws-events/test/test.rule.ts b/packages/@aws-cdk/aws-events/test/test.rule.ts index 3ae1f5acc8e40..ec5b7f0a8c51d 100644 --- a/packages/@aws-cdk/aws-events/test/test.rule.ts +++ b/packages/@aws-cdk/aws-events/test/test.rule.ts @@ -1,8 +1,9 @@ import { expect } from '@aws-cdk/assert'; import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); +import { resolve } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; -import { IEventRuleTarget } from '../lib'; +import { IEventRuleTarget, RuleArn } from '../lib'; import { EventRule } from '../lib/rule'; // tslint:disable:object-literal-key-quotes @@ -137,19 +138,19 @@ export = { 'targets can be added via props or addTarget with input transformer'(test: Test) { const stack = new cdk.Stack(); const t1: IEventRuleTarget = { - eventRuleTarget: { + asEventRuleTarget: () => ({ id: 'T1', arn: new cdk.Arn('ARN1'), kinesisParameters: { partitionKeyPath: 'partitionKeyPath' } - } + }) }; const t2: IEventRuleTarget = { - eventRuleTarget: { + asEventRuleTarget: () => ({ id: 'T2', arn: new cdk.Arn('ARN2'), roleArn: new iam.RoleArn('IAM-ROLE-ARN') - } + }) }; const rule = new EventRule(stack, 'EventRule', { @@ -201,12 +202,14 @@ export = { 'input template can contain tokens'(test: Test) { const stack = new cdk.Stack(); const t1: IEventRuleTarget = { - eventRuleTarget: { id: 'T1', arn: new cdk.Arn('ARN1'), kinesisParameters: { partitionKeyPath: 'partitionKeyPath' } } + asEventRuleTarget: () => ({ + id: 'T1', arn: new cdk.Arn('ARN1'), kinesisParameters: { partitionKeyPath: 'partitionKeyPath' } + }) }; - const t2: IEventRuleTarget = { eventRuleTarget: { id: 'T2', arn: new cdk.Arn('ARN2'), roleArn: new iam.RoleArn('IAM-ROLE-ARN') } }; - const t3: IEventRuleTarget = { eventRuleTarget: { id: 'T3', arn: new cdk.Arn('ARN3') } }; - const t4: IEventRuleTarget = { eventRuleTarget: { id: 'T4', arn: new cdk.Arn('ARN4') } }; + const t2: IEventRuleTarget = { asEventRuleTarget: () => ({ id: 'T2', arn: new cdk.Arn('ARN2'), roleArn: new iam.RoleArn('IAM-ROLE-ARN') }) }; + const t3: IEventRuleTarget = { asEventRuleTarget: () => ({ id: 'T3', arn: new cdk.Arn('ARN3') }) }; + const t4: IEventRuleTarget = { asEventRuleTarget: () => ({ id: 'T4', arn: new cdk.Arn('ARN4') }) }; const rule = new EventRule(stack, 'EventRule'); @@ -310,6 +313,33 @@ export = { } }); + test.done(); + }, + + 'asEventRuleTarget can use the ruleArn and a uniqueId of the rule'(test: Test) { + const stack = new cdk.Stack(); + + let receivedRuleArn = new RuleArn('FAIL'); + let receivedRuleId = 'FAIL'; + + const t1: IEventRuleTarget = { + asEventRuleTarget: (ruleArn: RuleArn, ruleId: string) => { + receivedRuleArn = ruleArn; + receivedRuleId = ruleId; + + return { + id: 'T1', + arn: new cdk.Arn('ARN1'), + kinesisParameters: { partitionKeyPath: 'partitionKeyPath' } + }; + } + }; + + const rule = new EventRule(stack, 'EventRule'); + rule.addTarget(t1); + + test.deepEqual(resolve(receivedRuleArn), resolve(rule.ruleArn)); + test.deepEqual(receivedRuleId, rule.uniqueId); test.done(); } }; diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts b/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts index 79402f1a4ff6f..87fae1518431a 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts @@ -131,12 +131,6 @@ export abstract class FunctionRef extends cdk.Construct implements events.IEvent */ protected abstract readonly canCreatePermissions: boolean; - /** - * Indicates if the resource policy that allows CloudWatch events to publish - * notifications to this lambda have been added. - */ - private eventRuleTargetPolicyAdded = false; - /** * Indicates if the policy that allows CloudWatch logs to publish to this lambda has been added. */ @@ -177,14 +171,14 @@ export abstract class FunctionRef extends cdk.Construct implements events.IEvent * Returns a RuleTarget that can be used to trigger this Lambda as a * result from a CloudWatch event. */ - public get eventRuleTarget(): events.EventRuleTargetProps { - if (!this.eventRuleTargetPolicyAdded) { - this.addPermission('InvokedByCloudWatch', { + public asEventRuleTarget(ruleArn: events.RuleArn, ruleId: string): events.EventRuleTargetProps { + const permissionId = `AllowEventRule${ruleId}`; + if (!this.tryFindChild(permissionId)) { + this.addPermission(permissionId, { action: 'lambda:InvokeFunction', - principal: new cdk.ServicePrincipal('events.amazonaws.com') + principal: new cdk.ServicePrincipal('events.amazonaws.com'), + sourceArn: ruleArn }); - - this.eventRuleTargetPolicyAdded = true; } return { diff --git a/packages/@aws-cdk/aws-lambda/test/integ.events.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.events.expected.json new file mode 100644 index 0000000000000..82097f0e2e730 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/integ.events.expected.json @@ -0,0 +1,132 @@ +{ + "Resources": { + "MyFuncServiceRole54065130": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn", + ":", + { + "Ref": "AWS::Partition" + }, + ":", + "iam", + ":", + "", + ":", + "aws", + ":", + "policy", + "/", + "service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "MyFunc8A243A2C": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = function handler(event, _context, callback) {\n console.log(JSON.stringify(event, undefined, 2));\n return callback();\n}" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFuncServiceRole54065130", + "Arn" + ] + }, + "Runtime": "nodejs6.10" + }, + "DependsOn": [ + "MyFuncServiceRole54065130" + ] + }, + "MyFuncAllowEventRulelambdaeventsTimer0E6AB6D8E3B334A3": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunc8A243A2C" + }, + "Principal": "events.amazonaws.com", + "SourceArn": { + "Fn::GetAtt": [ + "TimerBF6F831F", + "Arn" + ] + } + } + }, + "MyFuncAllowEventRulelambdaeventsTimer27F866A1E0669C645": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunc8A243A2C" + }, + "Principal": "events.amazonaws.com", + "SourceArn": { + "Fn::GetAtt": [ + "Timer2B6F162E9", + "Arn" + ] + } + } + }, + "TimerBF6F831F": { + "Type": "AWS::Events::Rule", + "Properties": { + "ScheduleExpression": "rate(1 minute)", + "State": "ENABLED", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "MyFunc8A243A2C", + "Arn" + ] + }, + "Id": "MyFunc" + } + ] + } + }, + "Timer2B6F162E9": { + "Type": "AWS::Events::Rule", + "Properties": { + "ScheduleExpression": "rate(2 minutes)", + "State": "ENABLED", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "MyFunc8A243A2C", + "Arn" + ] + }, + "Id": "MyFunc" + } + ] + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/integ.events.ts b/packages/@aws-cdk/aws-lambda/test/integ.events.ts new file mode 100644 index 0000000000000..4c10cf5d32d0a --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/integ.events.ts @@ -0,0 +1,27 @@ +import events = require('@aws-cdk/aws-events'); +import cdk = require('@aws-cdk/cdk'); +import lambda = require('../lib'); + +const app = new cdk.App(process.argv); + +const stack = new cdk.Stack(app, 'lambda-events'); + +const fn = new lambda.Function(stack, 'MyFunc', { + runtime: lambda.Runtime.NodeJS610, + handler: 'index.handler', + code: lambda.Code.inline(`exports.handler = ${handler.toString()}`) +}); + +const timer = new events.EventRule(stack, 'Timer', { scheduleExpression: 'rate(1 minute)' }); +timer.addTarget(fn); + +const timer2 = new events.EventRule(stack, 'Timer2', { scheduleExpression: 'rate(2 minutes)' }); +timer2.addTarget(fn); + +process.stdout.write(app.run()); + +// tslint:disable:no-console +function handler(event: any, _context: any, callback: any) { + console.log(JSON.stringify(event, undefined, 2)); + return callback(); +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/integ.inline.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.inline.expected.json index 75224f4a2c1c7..93f3430530ed6 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.inline.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.inline.expected.json @@ -123,4 +123,4 @@ ] } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts index 095a3ecd08d0e..60af9d7dd575b 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts @@ -1,4 +1,4 @@ -import { expect, haveResource } from '@aws-cdk/assert'; +import { countResources, expect, haveResource } from '@aws-cdk/assert'; import events = require('@aws-cdk/aws-events'); import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); @@ -255,10 +255,12 @@ export = { // GIVEN const stack = new cdk.Stack(); const fn = newTestLambda(stack); - const rule = new events.EventRule(stack, 'Rule'); + const rule1 = new events.EventRule(stack, 'Rule'); + const rule2 = new events.EventRule(stack, 'Rule2'); // WHEN - rule.addTarget(fn); + rule1.addTarget(fn); + rule2.addTarget(fn); // THEN const lambdaId = "MyLambdaCCE802FB"; @@ -266,9 +268,18 @@ export = { expect(stack).to(haveResource('AWS::Lambda::Permission', { "Action": "lambda:InvokeFunction", "FunctionName": { "Ref": lambdaId }, - "Principal": "events.amazonaws.com" + "Principal": "events.amazonaws.com", + "SourceArn": { "Fn::GetAtt": [ "Rule4C995B7F", "Arn" ] } })); + expect(stack).to(haveResource('AWS::Lambda::Permission', { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Ref": "MyLambdaCCE802FB" }, + "Principal": "events.amazonaws.com", + "SourceArn": { "Fn::GetAtt": [ "Rule270732244", "Arn" ] } + })); + + expect(stack).to(countResources('AWS::Events::Rule', 2)); expect(stack).to(haveResource('AWS::Events::Rule', { "Targets": [ { diff --git a/packages/@aws-cdk/aws-sns/lib/topic-ref.ts b/packages/@aws-cdk/aws-sns/lib/topic-ref.ts index d12753da83ea6..c0a36dcb12dda 100644 --- a/packages/@aws-cdk/aws-sns/lib/topic-ref.ts +++ b/packages/@aws-cdk/aws-sns/lib/topic-ref.ts @@ -205,8 +205,10 @@ export abstract class TopicRef extends cdk.Construct implements events.IEventRul /** * Returns a RuleTarget that can be used to trigger this SNS topic as a * result from a CloudWatch event. + * + * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/resource-based-policies-cwe.html#sns-permissions */ - public get eventRuleTarget(): events.EventRuleTargetProps { + public asEventRuleTarget(_ruleArn: events.RuleArn, _ruleId: string): events.EventRuleTargetProps { if (!this.eventRuleTargetPolicyAdded) { this.addToResourcePolicy(new cdk.PolicyStatement() .addAction('sns:Publish') From 5dfa7855b46a4bb09d4ccf3d7c9eba47707efc6c Mon Sep 17 00:00:00 2001 From: Romain Marcadier-Muller Date: Tue, 14 Aug 2018 12:58:08 +0200 Subject: [PATCH 10/33] fix: gracefully handle absence fo the `~/.aws/credentials` file (#541) The JS SDK assumes the file exists when it is passed as an argument, so this change makes the CDK Toolkit check for file existence before passing down to the SDK layer. Additionally, the SDK "ini file" re-implementation failed to check for file existece before attempting to load file contents. Fixes #540 --- packages/aws-cdk/bin/cdk.ts | 2 +- packages/aws-cdk/lib/api/util/sdk.ts | 21 +++++++++++-------- packages/aws-cdk/lib/api/util/sdk_ini_file.ts | 15 ++++++------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/aws-cdk/bin/cdk.ts b/packages/aws-cdk/bin/cdk.ts index e73e37f8ff994..9aaaa73f05721 100644 --- a/packages/aws-cdk/bin/cdk.ts +++ b/packages/aws-cdk/bin/cdk.ts @@ -729,7 +729,7 @@ async function initCommandLine() { */ async function populateDefaultEnvironmentIfNeeded(context: any) { if (!(cxapi.DEFAULT_REGION_CONTEXT_KEY in context)) { - context[cxapi.DEFAULT_REGION_CONTEXT_KEY] = aws.defaultRegion(); + context[cxapi.DEFAULT_REGION_CONTEXT_KEY] = await aws.defaultRegion(); debug(`Setting "${cxapi.DEFAULT_REGION_CONTEXT_KEY}" context to`, context[cxapi.DEFAULT_REGION_CONTEXT_KEY]); } diff --git a/packages/aws-cdk/lib/api/util/sdk.ts b/packages/aws-cdk/lib/api/util/sdk.ts index cfcde34f1cd14..49e24cb84658a 100644 --- a/packages/aws-cdk/lib/api/util/sdk.ts +++ b/packages/aws-cdk/lib/api/util/sdk.ts @@ -1,5 +1,6 @@ import { Environment} from '@aws-cdk/cx-api'; import AWS = require('aws-sdk'); +import fs = require('fs-extra'); import os = require('os'); import path = require('path'); import { debug } from '../../logging'; @@ -22,13 +23,12 @@ export class SDK { private defaultAccountId?: string = undefined; private readonly userAgent: string; private readonly accountCache = new AccountAccessKeyCache(); - private readonly defaultCredentialProvider: AWS.CredentialProviderChain; + private defaultCredentialProvider?: AWS.CredentialProviderChain; constructor(private readonly profile: string | undefined) { // Find the package.json from the main toolkit const pkg = (require.main as any).require('../package.json'); this.userAgent = `${pkg.name}/${pkg.version}`; - this.defaultCredentialProvider = makeCLICompatibleCredentialProvider(profile); } public async cloudFormation(environment: Environment, mode: Mode): Promise { @@ -63,8 +63,8 @@ export class SDK { }); } - public defaultRegion(): string | undefined { - return getCLICompatibleDefaultRegion(this.profile); + public async defaultRegion(): Promise { + return await getCLICompatibleDefaultRegion(this.profile); } public async defaultAccount(): Promise { @@ -78,6 +78,9 @@ export class SDK { private async lookupDefaultAccount() { try { debug('Resolving default credentials'); + if (!this.defaultCredentialProvider) { + this.defaultCredentialProvider = await makeCLICompatibleCredentialProvider(this.profile); + } const creds = await this.defaultCredentialProvider.resolvePromise(); const accessKeyId = creds.accessKeyId; if (!accessKeyId) { @@ -147,7 +150,7 @@ export class SDK { * file location is not given (SDK expects explicit environment variable with name). * - AWS_DEFAULT_PROFILE is also inspected for profile name (not just AWS_PROFILE). */ -function makeCLICompatibleCredentialProvider(profile: string | undefined) { +async function makeCLICompatibleCredentialProvider(profile: string | undefined) { profile = profile || process.env.AWS_PROFILE || process.env.AWS_DEFAULT_PROFILE || 'default'; // Need to construct filename ourselves, without appropriate environment variables @@ -157,7 +160,7 @@ function makeCLICompatibleCredentialProvider(profile: string | undefined) { return new AWS.CredentialProviderChain([ () => new AWS.EnvironmentCredentials('AWS'), () => new AWS.EnvironmentCredentials('AMAZON'), - () => new AWS.SharedIniFileCredentials({ profile, filename }), + ...(await fs.pathExists(filename) ? [() => new AWS.SharedIniFileCredentials({ profile, filename })] : []), () => { // Calling private API if ((AWS.ECSCredentials.prototype as any).isConfiguredForEcsCredentials()) { @@ -181,7 +184,7 @@ function makeCLICompatibleCredentialProvider(profile: string | undefined) { * - AWS_DEFAULT_PROFILE and AWS_DEFAULT_REGION are also used as environment * variables to be used to determine the region. */ -function getCLICompatibleDefaultRegion(profile: string | undefined): string | undefined { +async function getCLICompatibleDefaultRegion(profile: string | undefined): Promise { profile = profile || process.env.AWS_PROFILE || process.env.AWS_DEFAULT_PROFILE || 'default'; // Defaults inside constructor @@ -195,9 +198,9 @@ function getCLICompatibleDefaultRegion(profile: string | undefined): string | un while (!region && toCheck.length > 0) { const configFile = new SharedIniFile(toCheck.shift()); - const section = configFile.getProfile(profile); + const section = await configFile.getProfile(profile); region = section && section.region; } return region; -} \ No newline at end of file +} diff --git a/packages/aws-cdk/lib/api/util/sdk_ini_file.ts b/packages/aws-cdk/lib/api/util/sdk_ini_file.ts index fc21bac2b0e49..826a5a5ffe992 100644 --- a/packages/aws-cdk/lib/api/util/sdk_ini_file.ts +++ b/packages/aws-cdk/lib/api/util/sdk_ini_file.ts @@ -6,6 +6,7 @@ */ import AWS = require('aws-sdk'); +import fs = require('fs-extra'); import os = require('os'); import path = require('path'); @@ -25,8 +26,8 @@ export class SharedIniFile { this.filename = options.filename || this.getDefaultFilepath(); } - public getProfile(profile: string) { - this.ensureFileLoaded(); + public async getProfile(profile: string) { + await this.ensureFileLoaded(); const profileIndex = profile !== (AWS as any).util.defaultProfile && this.isConfig ? 'profile ' + profile : profile; @@ -42,11 +43,11 @@ export class SharedIniFile { ); } - private ensureFileLoaded() { + private async ensureFileLoaded() { if (!this.parsedContents) { - this.parsedContents = (AWS as any).util.ini.parse( - (AWS as any).util.readFileSync(this.filename) - ); + this.parsedContents = await fs.pathExists(this.filename) + ? (AWS as any).util.ini.parse(await fs.readFile(this.filename)) + : {}; } } -} \ No newline at end of file +} From 030a7e77a40d73307a0751225834ed11d72f4ce7 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Tue, 14 Aug 2018 14:05:31 +0300 Subject: [PATCH 11/33] feat(aws-sns): bucket notification destination (#560) Allow SNS topics to be used as bucket notification destinations. To avoid taking a dependency on aws-s3, extracted the bucket notification destination API into a separate module @aws-cdk/aws-s3-notifications, which only includes the required interfaces. We still take a devDependency on s3, but that's fine. Added examples/** to the global .nycrc --- .../@aws-cdk/aws-s3-notifications/.gitignore | 15 ++ .../@aws-cdk/aws-s3-notifications/.npmignore | 14 ++ .../@aws-cdk/aws-s3-notifications/LICENSE | 201 +++++++++++++++++ packages/@aws-cdk/aws-s3-notifications/NOTICE | 2 + .../@aws-cdk/aws-s3-notifications/README.md | 9 + .../lib/destination.ts} | 11 +- .../aws-s3-notifications/lib/index.ts | 1 + .../aws-s3-notifications/package.json | 53 +++++ packages/@aws-cdk/aws-s3/lib/bucket.ts | 2 +- packages/@aws-cdk/aws-s3/lib/index.ts | 1 - .../notifications-resource.ts | 4 +- packages/@aws-cdk/aws-s3/package.json | 3 +- .../aws-s3/test/notification-dests.ts | 14 +- .../aws-s3/test/test.notifications.ts | 19 +- packages/@aws-cdk/aws-sns/lib/topic-ref.ts | 32 ++- packages/@aws-cdk/aws-sns/package.json | 2 + ...teg.sns-bucket-notifications.expected.json | 213 ++++++++++++++++++ .../test/integ.sns-bucket-notifications.ts | 23 ++ packages/@aws-cdk/aws-sns/test/test.sns.ts | 82 +++++++ tools/cdk-build-tools/config/nycrc | 1 + 20 files changed, 676 insertions(+), 26 deletions(-) create mode 100644 packages/@aws-cdk/aws-s3-notifications/.gitignore create mode 100644 packages/@aws-cdk/aws-s3-notifications/.npmignore create mode 100644 packages/@aws-cdk/aws-s3-notifications/LICENSE create mode 100644 packages/@aws-cdk/aws-s3-notifications/NOTICE create mode 100644 packages/@aws-cdk/aws-s3-notifications/README.md rename packages/@aws-cdk/{aws-s3/lib/notification-dest.ts => aws-s3-notifications/lib/destination.ts} (63%) create mode 100644 packages/@aws-cdk/aws-s3-notifications/lib/index.ts create mode 100644 packages/@aws-cdk/aws-s3-notifications/package.json create mode 100644 packages/@aws-cdk/aws-sns/test/integ.sns-bucket-notifications.expected.json create mode 100644 packages/@aws-cdk/aws-sns/test/integ.sns-bucket-notifications.ts diff --git a/packages/@aws-cdk/aws-s3-notifications/.gitignore b/packages/@aws-cdk/aws-s3-notifications/.gitignore new file mode 100644 index 0000000000000..acfc6e27248fe --- /dev/null +++ b/packages/@aws-cdk/aws-s3-notifications/.gitignore @@ -0,0 +1,15 @@ +*.js +tsconfig.json +tslint.json +*.js.map +*.d.ts +*.generated.ts +dist +lib/generated/resources.ts +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-notifications/.npmignore b/packages/@aws-cdk/aws-s3-notifications/.npmignore new file mode 100644 index 0000000000000..e511c5acc268d --- /dev/null +++ b/packages/@aws-cdk/aws-s3-notifications/.npmignore @@ -0,0 +1,14 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii diff --git a/packages/@aws-cdk/aws-s3-notifications/LICENSE b/packages/@aws-cdk/aws-s3-notifications/LICENSE new file mode 100644 index 0000000000000..1739faaebb745 --- /dev/null +++ b/packages/@aws-cdk/aws-s3-notifications/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-s3-notifications/NOTICE b/packages/@aws-cdk/aws-s3-notifications/NOTICE new file mode 100644 index 0000000000000..95fd48569c743 --- /dev/null +++ b/packages/@aws-cdk/aws-s3-notifications/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-s3-notifications/README.md b/packages/@aws-cdk/aws-s3-notifications/README.md new file mode 100644 index 0000000000000..7d4272d8fc2f2 --- /dev/null +++ b/packages/@aws-cdk/aws-s3-notifications/README.md @@ -0,0 +1,9 @@ +## S3 Bucket Notifications API + +This module includes the API that constructs should implement in order to be +able to be used as destinations for bucket notifications. + +To implement the `IBucketNotificationDestination`, a construct should implement +a method `asBucketNotificationDestination(bucketArn, bucketId)` which registers +this resource as a destination for bucket notifications _for the specified +bucket_ and returns the ARN of the destination and it's type. diff --git a/packages/@aws-cdk/aws-s3/lib/notification-dest.ts b/packages/@aws-cdk/aws-s3-notifications/lib/destination.ts similarity index 63% rename from packages/@aws-cdk/aws-s3/lib/notification-dest.ts rename to packages/@aws-cdk/aws-s3-notifications/lib/destination.ts index 2e16e1aa1e7f3..d48960a02c82d 100644 --- a/packages/@aws-cdk/aws-s3/lib/notification-dest.ts +++ b/packages/@aws-cdk/aws-s3-notifications/lib/destination.ts @@ -1,15 +1,18 @@ import cdk = require('@aws-cdk/cdk'); -import { Bucket } from './bucket'; /** * Implemented by constructs that can be used as bucket notification destinations. */ export interface IBucketNotificationDestination { /** - * Registers this resource to receive notifications for the specified bucket. - * @param bucket The bucket. Use the `path` of the bucket as a unique ID. + * Registers this resource to receive notifications for the specified + * bucket. This method will only be called once for each destination/bucket + * pair and the result will be cached, so there is no need to implement + * idempotency in each destination. + * @param bucketArn The ARN of the bucket + * @param bucketId A unique ID of this bucket in the stack */ - asBucketNotificationDestination(bucket: Bucket): BucketNotificationDestinationProps; + asBucketNotificationDestination(bucketArn: cdk.Arn, bucketId: string): BucketNotificationDestinationProps; } /** diff --git a/packages/@aws-cdk/aws-s3-notifications/lib/index.ts b/packages/@aws-cdk/aws-s3-notifications/lib/index.ts new file mode 100644 index 0000000000000..35b7e2954ac1f --- /dev/null +++ b/packages/@aws-cdk/aws-s3-notifications/lib/index.ts @@ -0,0 +1 @@ +export * from './destination'; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3-notifications/package.json b/packages/@aws-cdk/aws-s3-notifications/package.json new file mode 100644 index 0000000000000..390d43d9af0ba --- /dev/null +++ b/packages/@aws-cdk/aws-s3-notifications/package.json @@ -0,0 +1,53 @@ +{ + "name": "@aws-cdk/aws-s3-notifications", + "version": "0.8.1", + "description": "Bucket Notifications API for AWS S3", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "targets": { + "java": { + "package": "software.amazon.awscdk.services.s3.notifications", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "s3-notifications" + } + }, + "sphinx": {} + } + }, + "repository": { + "type": "git", + "url": "https://github.com/awslabs/aws-cdk.git" + }, + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "cdk-integ", + "pkglint": "pkglint -f", + "package": "cdk-package" + }, + "keywords": [ + "aws", + "cdk", + "s3", + "notifications" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "cdk-build-tools": "^0.8.1", + "pkglint": "^0.8.1" + }, + "dependencies": { + "@aws-cdk/cdk": "^0.8.1" + }, + "homepage": "https://github.com/awslabs/aws-cdk" +} diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index b2f9fb390c47b..9ecb1da168a64 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -1,8 +1,8 @@ import iam = require('@aws-cdk/aws-iam'); import kms = require('@aws-cdk/aws-kms'); +import { IBucketNotificationDestination } from '@aws-cdk/aws-s3-notifications'; import cdk = require('@aws-cdk/cdk'); import { BucketPolicy } from './bucket-policy'; -import { IBucketNotificationDestination } from './notification-dest'; import { BucketNotifications } from './notifications-resource'; import perms = require('./perms'); import { LifecycleRule } from './rule'; diff --git a/packages/@aws-cdk/aws-s3/lib/index.ts b/packages/@aws-cdk/aws-s3/lib/index.ts index e0d99e5e7ebe1..593c797757b3f 100644 --- a/packages/@aws-cdk/aws-s3/lib/index.ts +++ b/packages/@aws-cdk/aws-s3/lib/index.ts @@ -1,7 +1,6 @@ export * from './bucket'; export * from './bucket-policy'; export * from './rule'; -export * from './notification-dest'; // AWS::S3 CloudFormation Resources: export * from './s3.generated'; diff --git a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts index fb14744f32346..309f845c8df5b 100644 --- a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts +++ b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts @@ -1,6 +1,6 @@ +import { BucketNotificationDestinationType, IBucketNotificationDestination } from '@aws-cdk/aws-s3-notifications'; import cdk = require('@aws-cdk/cdk'); import { Bucket, EventType, NotificationKeyFilter } from '../bucket'; -import { BucketNotificationDestinationType, IBucketNotificationDestination } from '../notification-dest'; import { NotificationsResourceHandler } from './notifications-resource-handler'; interface NotificationsProps { @@ -53,7 +53,7 @@ export class BucketNotifications extends cdk.Construct { // resolve target. this also provides an opportunity for the target to e.g. update // policies to allow this notification to happen. - const targetProps = target.asBucketNotificationDestination(this.bucket); + const targetProps = target.asBucketNotificationDestination(this.bucket.bucketArn, this.bucket.path); const commonConfig: CommonConfiguration = { Events: [ event ], Filter: renderFilters(filters), diff --git a/packages/@aws-cdk/aws-s3/package.json b/packages/@aws-cdk/aws-s3/package.json index f5639f2bac27e..4490542d0cb4a 100644 --- a/packages/@aws-cdk/aws-s3/package.json +++ b/packages/@aws-cdk/aws-s3/package.json @@ -55,7 +55,8 @@ "dependencies": { "@aws-cdk/aws-iam": "^0.8.1", "@aws-cdk/aws-kms": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.1", + "@aws-cdk/aws-s3-notifications": "^0.8.1" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-s3/test/notification-dests.ts b/packages/@aws-cdk/aws-s3/test/notification-dests.ts index afdf24035bc4c..c707a38dfd12c 100644 --- a/packages/@aws-cdk/aws-s3/test/notification-dests.ts +++ b/packages/@aws-cdk/aws-s3/test/notification-dests.ts @@ -1,11 +1,11 @@ +import s3notifications = require('@aws-cdk/aws-s3-notifications'); import cdk = require('@aws-cdk/cdk'); -import s3 = require('../lib'); /** * Since we can't take a dependency on @aws-cdk/sns, this is a simple wrapper * for AWS::SNS::Topic which implements IBucketNotificationDestination. */ -export class Topic extends cdk.Construct implements s3.IBucketNotificationDestination { +export class Topic extends cdk.Construct implements s3notifications.IBucketNotificationDestination { public readonly topicArn: cdk.Arn; private readonly policy = new cdk.PolicyDocument(); private readonly notifyingBucketPaths = new Set(); @@ -26,22 +26,22 @@ export class Topic extends cdk.Construct implements s3.IBucketNotificationDestin this.topicArn = resource.ref; } - public asBucketNotificationDestination(bucket: s3.Bucket): s3.BucketNotificationDestinationProps { + public asBucketNotificationDestination(bucketArn: cdk.Arn, bucketId: string): s3notifications.BucketNotificationDestinationProps { // add permission to each source bucket - if (!this.notifyingBucketPaths.has(bucket.path)) { + if (!this.notifyingBucketPaths.has(bucketId)) { this.policy.addStatement(new cdk.PolicyStatement() .describe(`sid${this.policy.statementCount}`) .addServicePrincipal('s3.amazonaws.com') .addAction('sns:Publish') .addResource(this.topicArn) - .addCondition('ArnLike', { "aws:SourceArn": bucket.bucketArn })); - this.notifyingBucketPaths.add(bucket.path); + .addCondition('ArnLike', { "aws:SourceArn": bucketArn })); + this.notifyingBucketPaths.add(bucketId); } return { arn: this.topicArn, - type: s3.BucketNotificationDestinationType.Topic + type: s3notifications.BucketNotificationDestinationType.Topic }; } } diff --git a/packages/@aws-cdk/aws-s3/test/test.notifications.ts b/packages/@aws-cdk/aws-s3/test/test.notifications.ts index adbd7e7fef925..d8e210fe4fa30 100644 --- a/packages/@aws-cdk/aws-s3/test/test.notifications.ts +++ b/packages/@aws-cdk/aws-s3/test/test.notifications.ts @@ -1,4 +1,5 @@ import { expect, haveResource } from '@aws-cdk/assert'; +import s3n = require('@aws-cdk/aws-s3-notifications'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; import s3 = require('../lib'); @@ -93,23 +94,23 @@ export = { const bucket = new s3.Bucket(stack, 'TestBucket'); - const queueTarget: s3.IBucketNotificationDestination = { + const queueTarget: s3n.IBucketNotificationDestination = { asBucketNotificationDestination: _ => ({ - type: s3.BucketNotificationDestinationType.Queue, + type: s3n.BucketNotificationDestinationType.Queue, arn: new cdk.Arn('arn:aws:sqs:...') }) }; - const lambdaTarget: s3.IBucketNotificationDestination = { + const lambdaTarget: s3n.IBucketNotificationDestination = { asBucketNotificationDestination: _ => ({ - type: s3.BucketNotificationDestinationType.Lambda, + type: s3n.BucketNotificationDestinationType.Lambda, arn: new cdk.Arn('arn:aws:lambda:...') }) }; - const topicTarget: s3.IBucketNotificationDestination = { + const topicTarget: s3n.IBucketNotificationDestination = { asBucketNotificationDestination: _ => ({ - type: s3.BucketNotificationDestinationType.Topic, + type: s3n.BucketNotificationDestinationType.Topic, arn: new cdk.Arn('arn:aws:sns:...') }) }; @@ -176,14 +177,14 @@ export = { bucket.onEvent(s3.EventType.ObjectRemovedDelete, { asBucketNotificationDestination: _ => ({ - type: s3.BucketNotificationDestinationType.Queue, + type: s3n.BucketNotificationDestinationType.Queue, arn: new cdk.Arn('arn:aws:sqs:...:queue1') }) }); bucket.onEvent(s3.EventType.ObjectRemovedDelete, { asBucketNotificationDestination: _ => ({ - type: s3.BucketNotificationDestinationType.Queue, + type: s3n.BucketNotificationDestinationType.Queue, arn: new cdk.Arn('arn:aws:sqs:...:queue2') }) }); @@ -225,7 +226,7 @@ export = { const bucket = new s3.Bucket(stack, 'TestBucket'); const bucketNotificationTarget = { - type: s3.BucketNotificationDestinationType.Queue, + type: s3n.BucketNotificationDestinationType.Queue, arn: new cdk.Arn('arn:aws:sqs:...') }; diff --git a/packages/@aws-cdk/aws-sns/lib/topic-ref.ts b/packages/@aws-cdk/aws-sns/lib/topic-ref.ts index c0a36dcb12dda..c5476d05c4cb9 100644 --- a/packages/@aws-cdk/aws-sns/lib/topic-ref.ts +++ b/packages/@aws-cdk/aws-sns/lib/topic-ref.ts @@ -2,6 +2,7 @@ import cloudwatch = require('@aws-cdk/aws-cloudwatch'); import events = require('@aws-cdk/aws-events'); import iam = require('@aws-cdk/aws-iam'); import lambda = require('@aws-cdk/aws-lambda'); +import s3n = require('@aws-cdk/aws-s3-notifications'); import sqs = require('@aws-cdk/aws-sqs'); import cdk = require('@aws-cdk/cdk'); import { TopicPolicy } from './policy'; @@ -16,7 +17,7 @@ export class TopicArn extends cdk.Arn { } /** * Either a new or imported Topic */ -export abstract class TopicRef extends cdk.Construct implements events.IEventRuleTarget, cloudwatch.IAlarmAction { +export abstract class TopicRef extends cdk.Construct implements events.IEventRuleTarget, cloudwatch.IAlarmAction, s3n.IBucketNotificationDestination { /** * Import a Topic defined elsewhere */ @@ -37,6 +38,9 @@ export abstract class TopicRef extends cdk.Construct implements events.IEventRul private policy?: TopicPolicy; + /** Buckets permitted to send notifications to this topic */ + private readonly notifyingBuckets = new Set(); + /** * Indicates if the resource policy that allows CloudWatch events to publish * notifications to this topic have been added. @@ -275,6 +279,32 @@ export abstract class TopicRef extends cdk.Construct implements events.IEventRul public metricNumberOfMessagesDelivered(props?: cloudwatch.MetricCustomization): cloudwatch.Metric { return this.metric('NumberOfMessagesDelivered', { statistic: 'sum', ...props }); } + + /** + * Implements the IBucketNotificationDestination interface, allowing topics to be used + * as bucket notification destinations. + * + * @param bucketArn The ARN of the bucket sending the notifications + * @param bucketId A unique ID of the bucket + */ + public asBucketNotificationDestination(bucketArn: cdk.Arn, bucketId: string): s3n.BucketNotificationDestinationProps { + // allow this bucket to sns:publish to this topic (if it doesn't already have a permission) + if (!this.notifyingBuckets.has(bucketId)) { + + this.addToResourcePolicy(new cdk.PolicyStatement() + .addServicePrincipal('s3.amazonaws.com') + .addAction('sns:Publish') + .addResource(this.topicArn) + .addCondition('ArnLike', { "aws:SourceArn": bucketArn })); + + this.notifyingBuckets.add(bucketId); + } + + return { + arn: this.topicArn, + type: s3n.BucketNotificationDestinationType.Topic + }; + } } /** diff --git a/packages/@aws-cdk/aws-sns/package.json b/packages/@aws-cdk/aws-sns/package.json index ecd7c5fda6441..0894e119e8399 100644 --- a/packages/@aws-cdk/aws-sns/package.json +++ b/packages/@aws-cdk/aws-sns/package.json @@ -47,6 +47,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/assert": "^0.8.1", + "@aws-cdk/aws-s3": "^0.8.1", "cdk-build-tools": "^0.8.1", "cdk-integ-tools": "^0.8.1", "cfn2ts": "^0.8.1", @@ -58,6 +59,7 @@ "@aws-cdk/aws-iam": "^0.8.1", "@aws-cdk/aws-lambda": "^0.8.1", "@aws-cdk/aws-sqs": "^0.8.1", + "@aws-cdk/aws-s3-notifications": "^0.8.1", "@aws-cdk/cdk": "^0.8.1" }, "homepage": "https://github.com/awslabs/aws-cdk" diff --git a/packages/@aws-cdk/aws-sns/test/integ.sns-bucket-notifications.expected.json b/packages/@aws-cdk/aws-sns/test/integ.sns-bucket-notifications.expected.json new file mode 100644 index 0000000000000..011685cf9af87 --- /dev/null +++ b/packages/@aws-cdk/aws-sns/test/integ.sns-bucket-notifications.expected.json @@ -0,0 +1,213 @@ +{ + "Resources": { + "ObjectCreatedTopic92F47E19": { + "Type": "AWS::SNS::Topic" + }, + "ObjectCreatedTopicPolicyA938ECFC": { + "Type": "AWS::SNS::TopicPolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "s3.amazonaws.com" + }, + "Resource": { + "Ref": "ObjectCreatedTopic92F47E19" + }, + "Sid": "0" + } + ], + "Version": "2012-10-17" + }, + "Topics": [ + { + "Ref": "ObjectCreatedTopic92F47E19" + } + ] + } + }, + "ObjectDeletedTopic2A914EC0": { + "Type": "AWS::SNS::Topic" + }, + "ObjectDeletedTopicPolicy026B02E6": { + "Type": "AWS::SNS::TopicPolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "s3.amazonaws.com" + }, + "Resource": { + "Ref": "ObjectDeletedTopic2A914EC0" + }, + "Sid": "0" + } + ], + "Version": "2012-10-17" + }, + "Topics": [ + { + "Ref": "ObjectDeletedTopic2A914EC0" + } + ] + } + }, + "MyBucketF68F3FF0": { + "Type": "AWS::S3::Bucket" + }, + "MyBucketNotifications46AC0CD2": { + "Type": "Custom::S3BucketNotifications", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", + "Arn" + ] + }, + "BucketName": { + "Ref": "MyBucketF68F3FF0" + }, + "NotificationConfiguration": { + "TopicConfigurations": [ + { + "Events": [ + "s3:ObjectCreated:*" + ], + "TopicArn": { + "Ref": "ObjectCreatedTopic92F47E19" + } + }, + { + "Events": [ + "s3:ObjectRemoved:*" + ], + "Filter": { + "Key": { + "FilterRules": [ + { + "Name": "suffix", + "Value": ".txt" + }, + { + "Name": "prefix", + "Value": "foo/" + } + ] + } + }, + "TopicArn": { + "Ref": "ObjectDeletedTopic2A914EC0" + } + } + ] + } + } + }, + "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn", + ":", + { + "Ref": "AWS::Partition" + }, + ":", + "iam", + ":", + "", + ":", + "aws", + ":", + "policy", + "/", + "service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleDefaultPolicy2CF63D36": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutBucketNotification", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleDefaultPolicy2CF63D36", + "Roles": [ + { + "Ref": "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC" + } + ] + } + }, + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", + "Code": { + "ZipFile": "exports.handler = (event, context) => {\n const s3 = new (require('aws-sdk').S3)();\n const https = require(\"https\");\n const url = require(\"url\");\n log(JSON.stringify(event, undefined, 2));\n const props = event.ResourceProperties;\n if (event.RequestType === 'Delete') {\n props.NotificationConfiguration = {}; // this is how you clean out notifications\n }\n const req = {\n Bucket: props.BucketName,\n NotificationConfiguration: props.NotificationConfiguration\n };\n return s3.putBucketNotificationConfiguration(req, (err, data) => {\n log({ err, data });\n if (err) {\n return submitResponse(\"FAILED\", err.message + `\\nMore information in CloudWatch Log Stream: ${context.logStreamName}`);\n }\n else {\n return submitResponse(\"SUCCESS\");\n }\n });\n function log(obj) {\n console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj);\n }\n // tslint:disable-next-line:max-line-length\n // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule\n // to allow sending an error messge as a reason.\n function submitResponse(responseStatus, reason) {\n const responseBody = JSON.stringify({\n Status: responseStatus,\n Reason: reason || \"See the details in CloudWatch Log Stream: \" + context.logStreamName,\n PhysicalResourceId: context.logStreamName,\n StackId: event.StackId,\n RequestId: event.RequestId,\n LogicalResourceId: event.LogicalResourceId,\n NoEcho: false,\n });\n log({ responseBody });\n const parsedUrl = url.parse(event.ResponseURL);\n const options = {\n hostname: parsedUrl.hostname,\n port: 443,\n path: parsedUrl.path,\n method: \"PUT\",\n headers: {\n \"content-type\": \"\",\n \"content-length\": responseBody.length\n }\n };\n const request = https.request(options, (r) => {\n log({ statusCode: r.statusCode, statusMessage: r.statusMessage });\n context.done();\n });\n request.on(\"error\", (error) => {\n log({ sendError: error });\n context.done();\n });\n request.write(responseBody);\n request.end();\n }\n};" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Timeout": 300 + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sns/test/integ.sns-bucket-notifications.ts b/packages/@aws-cdk/aws-sns/test/integ.sns-bucket-notifications.ts new file mode 100644 index 0000000000000..2376cdc41a92c --- /dev/null +++ b/packages/@aws-cdk/aws-sns/test/integ.sns-bucket-notifications.ts @@ -0,0 +1,23 @@ +import s3 = require('@aws-cdk/aws-s3'); +import cdk = require('@aws-cdk/cdk'); +import sns = require('../lib'); + +class MyStack extends cdk.Stack { + constructor(parent: cdk.App, id: string) { + super(parent, id); + + const objectCreateTopic = new sns.Topic(this, 'ObjectCreatedTopic'); + const objectRemovedTopic = new sns.Topic(this, 'ObjectDeletedTopic'); + const bucket = new s3.Bucket(this, 'MyBucket'); + + bucket.onObjectCreated(objectCreateTopic); + bucket.onObjectRemoved(objectRemovedTopic, { prefix: 'foo/', suffix: '.txt' }); + + } +} + +const app = new cdk.App(process.argv); + +new MyStack(app, 'sns-bucket-notifications'); + +process.stdout.write(app.run()); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sns/test/test.sns.ts b/packages/@aws-cdk/aws-sns/test/test.sns.ts index fdaf02b8ea88b..1b44251efb67c 100644 --- a/packages/@aws-cdk/aws-sns/test/test.sns.ts +++ b/packages/@aws-cdk/aws-sns/test/test.sns.ts @@ -2,8 +2,10 @@ import { expect, haveResource } from '@aws-cdk/assert'; import events = require('@aws-cdk/aws-events'); import iam = require('@aws-cdk/aws-iam'); import lambda = require('@aws-cdk/aws-lambda'); +import s3n = require('@aws-cdk/aws-s3-notifications'); import sqs = require('@aws-cdk/aws-sqs'); import cdk = require('@aws-cdk/cdk'); +import { resolve } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import sns = require('../lib'); @@ -687,5 +689,85 @@ export = { })); test.done(); + }, + + 'asBucketNotificationDestination adds bucket permissions only once for each bucket'(test: Test) { + const stack = new cdk.Stack(); + + const topic = new sns.Topic(stack, 'MyTopic'); + + const bucketArn = new cdk.Arn('arn:bucket'); + const bucketId = 'bucketId'; + + const dest1 = topic.asBucketNotificationDestination(bucketArn, bucketId); + test.deepEqual(resolve(dest1.arn), resolve(topic.topicArn)); + test.deepEqual(dest1.type, s3n.BucketNotificationDestinationType.Topic); + + // calling again on the same bucket yields is idempotent + const dest2 = topic.asBucketNotificationDestination(bucketArn, bucketId); + test.deepEqual(resolve(dest2.arn), resolve(topic.topicArn)); + test.deepEqual(dest2.type, s3n.BucketNotificationDestinationType.Topic); + + // another bucket will be added to the topic policy + const dest3 = topic.asBucketNotificationDestination(new cdk.Arn('bucket2'), 'bucket2'); + test.deepEqual(resolve(dest3.arn), resolve(topic.topicArn)); + test.deepEqual(dest3.type, s3n.BucketNotificationDestinationType.Topic); + + expect(stack).toMatch({ + "Resources": { + "MyTopic86869434": { + "Type": "AWS::SNS::Topic" + }, + "MyTopicPolicy12A5EC17": { + "Type": "AWS::SNS::TopicPolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Condition": { + "ArnLike": { + "aws:SourceArn": "arn:bucket" + } + }, + "Effect": "Allow", + "Principal": { + "Service": "s3.amazonaws.com" + }, + "Resource": { + "Ref": "MyTopic86869434" + }, + "Sid": "0" + }, + { + "Action": "sns:Publish", + "Condition": { + "ArnLike": { + "aws:SourceArn": "bucket2" + } + }, + "Effect": "Allow", + "Principal": { + "Service": "s3.amazonaws.com" + }, + "Resource": { + "Ref": "MyTopic86869434" + }, + "Sid": "1" + } + ], + "Version": "2012-10-17" + }, + "Topics": [ + { + "Ref": "MyTopic86869434" + } + ] + } + } + } + }); + + test.done(); } }; diff --git a/tools/cdk-build-tools/config/nycrc b/tools/cdk-build-tools/config/nycrc index edfbfb1b4f254..22a398e2c2183 100644 --- a/tools/cdk-build-tools/config/nycrc +++ b/tools/cdk-build-tools/config/nycrc @@ -11,6 +11,7 @@ "exclude": [ "coverage/**", "test/**", + "examples/**", "lib/*.generated.js", "build-tools/**" ] From 0cbb24708a14ce478ab1053bdaaeb419640d1428 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Tue, 14 Aug 2018 15:01:08 +0300 Subject: [PATCH 12/33] feat(aws-lambda): support bucket notifications (#561) Allow Lambda functions to be used as destinations for S3 bucket notifications. --- .../@aws-cdk/aws-lambda/lib/lambda-ref.ts | 31 ++- packages/@aws-cdk/aws-lambda/package.json | 1 + .../integ.bucket-notifications.expected.json | 256 ++++++++++++++++++ .../test/integ.bucket-notifications.ts | 28 ++ .../notifications-resource.ts | 2 +- 5 files changed, 313 insertions(+), 5 deletions(-) create mode 100644 packages/@aws-cdk/aws-lambda/test/integ.bucket-notifications.expected.json create mode 100644 packages/@aws-cdk/aws-lambda/test/integ.bucket-notifications.ts diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts b/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts index 87fae1518431a..862cacd238e6a 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts @@ -2,6 +2,7 @@ import cloudwatch = require('@aws-cdk/aws-cloudwatch'); import events = require('@aws-cdk/aws-events'); import iam = require('@aws-cdk/aws-iam'); import logs = require('@aws-cdk/aws-logs'); +import s3n = require('@aws-cdk/aws-s3-notifications'); import cdk = require('@aws-cdk/cdk'); import { cloudformation, FunctionArn } from './lambda.generated'; import { Permission } from './permission'; @@ -23,7 +24,9 @@ export interface FunctionRefProps { role?: iam.Role; } -export abstract class FunctionRef extends cdk.Construct implements events.IEventRuleTarget, logs.ILogSubscriptionDestination { +export abstract class FunctionRef extends cdk.Construct + implements events.IEventRuleTarget, logs.ILogSubscriptionDestination, s3n.IBucketNotificationDestination { + /** * Creates a Lambda function object which represents a function not defined * within this stack. @@ -138,9 +141,9 @@ export abstract class FunctionRef extends cdk.Construct implements events.IEvent /** * Adds a permission to the Lambda resource policy. - * @param name A name for the permission construct + * @param id The id ƒor the permission construct */ - public addPermission(name: string, permission: Permission) { + public addPermission(id: string, permission: Permission) { if (!this.canCreatePermissions) { // FIXME: Report metadata return; @@ -149,7 +152,7 @@ export abstract class FunctionRef extends cdk.Construct implements events.IEvent const principal = this.parsePermissionPrincipal(permission.principal); const action = permission.action || 'lambda:InvokeFunction'; - new cloudformation.PermissionResource(this, name, { + new cloudformation.PermissionResource(this, id, { action, principal, functionName: this.functionName, @@ -261,6 +264,26 @@ export abstract class FunctionRef extends cdk.Construct implements events.IEvent }; } + /** + * Allows this Lambda to be used as a destination for bucket notifications. + * Use `bucket.onEvent(lambda)` to subscribe. + */ + public asBucketNotificationDestination(bucketArn: cdk.Arn, bucketId: string): s3n.BucketNotificationDestinationProps { + const permissionId = `AllowBucketNotificationsFrom${bucketId}`; + if (!this.tryFindChild(permissionId)) { + this.addPermission(permissionId, { + sourceAccount: new cdk.AwsAccountId(), + principal: new cdk.ServicePrincipal('s3.amazonaws.com'), + sourceArn: bucketArn, + }); + } + + return { + type: s3n.BucketNotificationDestinationType.Lambda, + arn: this.functionArn + }; + } + private parsePermissionPrincipal(principal?: cdk.PolicyPrincipal) { if (!principal) { return undefined; diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index ab0f3b247c9e7..5a108e658cdcb 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -59,6 +59,7 @@ "@aws-cdk/aws-iam": "^0.8.1", "@aws-cdk/aws-logs": "^0.8.1", "@aws-cdk/aws-s3": "^0.8.1", + "@aws-cdk/aws-s3-notifications": "^0.8.1", "@aws-cdk/cdk": "^0.8.1", "@aws-cdk/cx-api": "^0.8.1" }, diff --git a/packages/@aws-cdk/aws-lambda/test/integ.bucket-notifications.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.bucket-notifications.expected.json new file mode 100644 index 0000000000000..edbf24da3f3f2 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/integ.bucket-notifications.expected.json @@ -0,0 +1,256 @@ +{ + "Resources": { + "MyBucketF68F3FF0": { + "Type": "AWS::S3::Bucket" + }, + "MyBucketNotifications46AC0CD2": { + "Type": "Custom::S3BucketNotifications", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", + "Arn" + ] + }, + "BucketName": { + "Ref": "MyBucketF68F3FF0" + }, + "NotificationConfiguration": { + "LambdaFunctionConfigurations": [ + { + "Events": [ + "s3:ObjectCreated:*" + ], + "Filter": { + "Key": { + "FilterRules": [ + { + "Name": "suffix", + "Value": ".png" + } + ] + } + }, + "LambdaFunctionArn": { + "Fn::GetAtt": [ + "MyFunction3BAA72D1", + "Arn" + ] + } + } + ] + } + } + }, + "MyFunctionServiceRole3C357FF2": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn", + ":", + { + "Ref": "AWS::Partition" + }, + ":", + "iam", + ":", + "", + ":", + "aws", + ":", + "policy", + "/", + "service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "MyFunction3BAA72D1": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = function handler(event, _context, callback) {\n console.log(JSON.stringify(event, undefined, 2));\n return callback(null, event);\n}" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionServiceRole3C357FF2", + "Arn" + ] + }, + "Runtime": "nodejs6.10" + }, + "DependsOn": [ + "MyFunctionServiceRole3C357FF2" + ] + }, + "MyFunctionAllowBucketNotificationsFromlambdabucketnotificationsMyBucket0F0FC402189522F6": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunction3BAA72D1" + }, + "Principal": "s3.amazonaws.com", + "SourceAccount": { + "Ref": "AWS::AccountId" + }, + "SourceArn": { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + } + } + }, + "MyFunctionAllowBucketNotificationsFromlambdabucketnotificationsYourBucket307F72F245F2C5AE": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunction3BAA72D1" + }, + "Principal": "s3.amazonaws.com", + "SourceAccount": { + "Ref": "AWS::AccountId" + }, + "SourceArn": { + "Fn::GetAtt": [ + "YourBucketC6A57364", + "Arn" + ] + } + } + }, + "YourBucketC6A57364": { + "Type": "AWS::S3::Bucket" + }, + "YourBucketNotifications8D39901A": { + "Type": "Custom::S3BucketNotifications", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", + "Arn" + ] + }, + "BucketName": { + "Ref": "YourBucketC6A57364" + }, + "NotificationConfiguration": { + "LambdaFunctionConfigurations": [ + { + "Events": [ + "s3:ObjectRemoved:*" + ], + "LambdaFunctionArn": { + "Fn::GetAtt": [ + "MyFunction3BAA72D1", + "Arn" + ] + } + } + ] + } + } + }, + "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn", + ":", + { + "Ref": "AWS::Partition" + }, + ":", + "iam", + ":", + "", + ":", + "aws", + ":", + "policy", + "/", + "service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleDefaultPolicy2CF63D36": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutBucketNotification", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleDefaultPolicy2CF63D36", + "Roles": [ + { + "Ref": "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC" + } + ] + } + }, + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", + "Code": { + "ZipFile": "exports.handler = (event, context) => {\n const s3 = new (require('aws-sdk').S3)();\n const https = require(\"https\");\n const url = require(\"url\");\n log(JSON.stringify(event, undefined, 2));\n const props = event.ResourceProperties;\n if (event.RequestType === 'Delete') {\n props.NotificationConfiguration = {}; // this is how you clean out notifications\n }\n const req = {\n Bucket: props.BucketName,\n NotificationConfiguration: props.NotificationConfiguration\n };\n return s3.putBucketNotificationConfiguration(req, (err, data) => {\n log({ err, data });\n if (err) {\n return submitResponse(\"FAILED\", err.message + `\\nMore information in CloudWatch Log Stream: ${context.logStreamName}`);\n }\n else {\n return submitResponse(\"SUCCESS\");\n }\n });\n function log(obj) {\n console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj);\n }\n // tslint:disable-next-line:max-line-length\n // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule\n // to allow sending an error messge as a reason.\n function submitResponse(responseStatus, reason) {\n const responseBody = JSON.stringify({\n Status: responseStatus,\n Reason: reason || \"See the details in CloudWatch Log Stream: \" + context.logStreamName,\n PhysicalResourceId: context.logStreamName,\n StackId: event.StackId,\n RequestId: event.RequestId,\n LogicalResourceId: event.LogicalResourceId,\n NoEcho: false,\n });\n log({ responseBody });\n const parsedUrl = url.parse(event.ResponseURL);\n const options = {\n hostname: parsedUrl.hostname,\n port: 443,\n path: parsedUrl.path,\n method: \"PUT\",\n headers: {\n \"content-type\": \"\",\n \"content-length\": responseBody.length\n }\n };\n const request = https.request(options, (r) => {\n log({ statusCode: r.statusCode, statusMessage: r.statusMessage });\n context.done();\n });\n request.on(\"error\", (error) => {\n log({ sendError: error });\n context.done();\n });\n request.write(responseBody);\n request.end();\n }\n};" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Timeout": 300 + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/integ.bucket-notifications.ts b/packages/@aws-cdk/aws-lambda/test/integ.bucket-notifications.ts new file mode 100644 index 0000000000000..22550911d4e71 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/integ.bucket-notifications.ts @@ -0,0 +1,28 @@ +import s3 = require('@aws-cdk/aws-s3'); +import cdk = require('@aws-cdk/cdk'); +import lambda = require('../lib'); + +const app = new cdk.App(process.argv); + +const stack = new cdk.Stack(app, 'lambda-bucket-notifications'); + +const bucketA = new s3.Bucket(stack, 'MyBucket'); + +const fn = new lambda.Function(stack, 'MyFunction', { + runtime: lambda.Runtime.NodeJS610, + handler: 'index.handler', + code: lambda.Code.inline(`exports.handler = ${handler.toString()}`) +}); + +const bucketB = new s3.Bucket(stack, 'YourBucket'); + +bucketA.onObjectCreated(fn, { suffix: '.png' }); +bucketB.onEvent(s3.EventType.ObjectRemoved, fn); + +process.stdout.write(app.run()); + +// tslint:disable:no-console +function handler(event: any, _context: any, callback: any) { + console.log(JSON.stringify(event, undefined, 2)); + return callback(null, event); +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts index 309f845c8df5b..3dec683c961a5 100644 --- a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts +++ b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts @@ -53,7 +53,7 @@ export class BucketNotifications extends cdk.Construct { // resolve target. this also provides an opportunity for the target to e.g. update // policies to allow this notification to happen. - const targetProps = target.asBucketNotificationDestination(this.bucket.bucketArn, this.bucket.path); + const targetProps = target.asBucketNotificationDestination(this.bucket.bucketArn, this.bucket.uniqueId); const commonConfig: CommonConfiguration = { Events: [ event ], Filter: renderFilters(filters), From 26d78edafd925cc99c280c0a586494c1b9e592fe Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Tue, 14 Aug 2018 18:31:46 +0300 Subject: [PATCH 13/33] fix(toolkit): "ini.split is not a function" error when reading aws config (#565) Since `readFile` returns a `Buffer` and `ini.parse` expects a string, we need `toString()`. Fixes #563 --- packages/aws-cdk/lib/api/util/sdk_ini_file.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/aws-cdk/lib/api/util/sdk_ini_file.ts b/packages/aws-cdk/lib/api/util/sdk_ini_file.ts index 826a5a5ffe992..84a8e4b6f0c1e 100644 --- a/packages/aws-cdk/lib/api/util/sdk_ini_file.ts +++ b/packages/aws-cdk/lib/api/util/sdk_ini_file.ts @@ -44,10 +44,16 @@ export class SharedIniFile { } private async ensureFileLoaded() { - if (!this.parsedContents) { - this.parsedContents = await fs.pathExists(this.filename) - ? (AWS as any).util.ini.parse(await fs.readFile(this.filename)) - : {}; + if (this.parsedContents) { + return; } + + if (!await fs.pathExists(this.filename)) { + this.parsedContents = {}; + return; + } + + const contents: string = (await fs.readFile(this.filename)).toString(); + this.parsedContents = (AWS as any).util.ini.parse(contents); } } From bde710ff7c0f2ef9b73ef4868f0ee2f738a6b27e Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 14 Aug 2018 09:07:08 -0700 Subject: [PATCH 14/33] docs: Added blurb about examples are in TypeScript by default (#552) * Added blurb about examples are in TypeScript by default * Update index.rst Changed "reference section for the service' to a link to the Reference section. --- docs/src/index.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/src/index.rst b/docs/src/index.rst index fb23b4d7d06a9..03e25af6c74ef 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -26,6 +26,13 @@ Developers can use one of the supported programming languages to define reusable cloud components called :doc:`constructs`, which are composed together into :doc:`stacks` and :doc:`apps`. +.. note:: + Unless otherwise indicated, + the code examples in this guide are in TypeScript. + + To aid you in porting a TypeScript example to a supported programming language, + take a look at the example code for your language in the :doc:`Reference section `. + The :ref:`AWS CDK Toolkit ` is a command-line tool for interacting with CDK apps. It allows developers to synthesize artifacts such as AWS CloudFormation Templates, deploy stacks to development AWS accounts and "diff" against a From 3524bf4975621d48ee19277e0af990e043d413db Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Tue, 14 Aug 2018 19:08:21 +0300 Subject: [PATCH 15/33] chore(cdk): Follow up on PR #556 (#562) * Use node.js "crypto" module instead of md5 * Remove `construct.name` (and fix all dependencies) * Remove non-alphanumeric after "Resource" is omitted BREAKING CHANGE: `construct.name` => `construct.id` --- .../lib/pipeline-actions.ts | 2 +- packages/@aws-cdk/aws-cloudtrail/lib/index.ts | 2 +- .../@aws-cdk/aws-codebuild/lib/project.ts | 2 +- .../@aws-cdk/aws-codepipeline/lib/actions.ts | 6 +- .../@aws-cdk/aws-codepipeline/lib/pipeline.ts | 8 +- .../@aws-cdk/aws-codepipeline/lib/stage.ts | 6 +- .../@aws-cdk/aws-lambda/lib/lambda-ref.ts | 2 +- packages/@aws-cdk/aws-sns/lib/topic-ref.ts | 12 +- .../cdk/lib/cloudformation/logical-id.ts | 2 +- .../@aws-cdk/cdk/lib/cloudformation/stack.ts | 9 +- packages/@aws-cdk/cdk/lib/core/construct.ts | 25 +- packages/@aws-cdk/cdk/lib/util/.gitignore | 2 - packages/@aws-cdk/cdk/lib/util/md5.js | 280 ------------------ packages/@aws-cdk/cdk/lib/util/uniqueid.ts | 23 +- .../@aws-cdk/cdk/test/core/test.construct.ts | 8 +- 15 files changed, 59 insertions(+), 330 deletions(-) delete mode 100644 packages/@aws-cdk/cdk/lib/util/.gitignore delete mode 100644 packages/@aws-cdk/cdk/lib/util/md5.js diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/lib/pipeline-actions.ts b/packages/@aws-cdk/aws-cloudformation-codepipeline/lib/pipeline-actions.ts index 7b5dee76168eb..69209fe58bae2 100644 --- a/packages/@aws-cdk/aws-cloudformation-codepipeline/lib/pipeline-actions.ts +++ b/packages/@aws-cdk/aws-cloudformation-codepipeline/lib/pipeline-actions.ts @@ -53,7 +53,7 @@ export abstract class CloudFormationAction extends codepipeline.DeployAction { }); if (props.outputFileName) { - this.artifact = this.addOutputArtifact(props.outputArtifactName || (parent.name + this.name + 'Artifact')); + this.artifact = this.addOutputArtifact(props.outputArtifactName || (parent.id + this.id + 'Artifact')); } } } diff --git a/packages/@aws-cdk/aws-cloudtrail/lib/index.ts b/packages/@aws-cdk/aws-cloudtrail/lib/index.ts index 5fbbe2605f1d7..0fc363819d073 100644 --- a/packages/@aws-cdk/aws-cloudtrail/lib/index.ts +++ b/packages/@aws-cdk/aws-cloudtrail/lib/index.ts @@ -174,7 +174,7 @@ export class CloudTrail extends cdk.Construct { includeGlobalServiceEvents: props.includeGlobalServiceEvents == null ? true : props.includeGlobalServiceEvents, trailName: props.trailName, kmsKeyId: props.kmsKey && props.kmsKey.keyArn, - s3BucketName: s3bucket.name, + s3BucketName: s3bucket.bucketName, s3KeyPrefix: props.s3KeyPrefix, cloudWatchLogsLogGroupArn: this.cloudWatchLogsGroupArn, cloudWatchLogsRoleArn: this.cloudWatchLogsRoleArn, diff --git a/packages/@aws-cdk/aws-codebuild/lib/project.ts b/packages/@aws-cdk/aws-codebuild/lib/project.ts index 1fc3925b3c26b..48120f478fa55 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -266,7 +266,7 @@ export abstract class ProjectRef extends cdk.Construct implements events.IEventR } return { - id: this.name, + id: this.id, arn: this.projectArn, roleArn: this.eventsRole.roleArn, }; diff --git a/packages/@aws-cdk/aws-codepipeline/lib/actions.ts b/packages/@aws-cdk/aws-codepipeline/lib/actions.ts index 5b89af927f753..805835c43fb8a 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/actions.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/actions.ts @@ -152,7 +152,7 @@ export abstract class Action extends cdk.Construct { */ public render(): cloudformation.PipelineResource.ActionDeclarationProperty { return { - name: this.name, + name: this.id, inputArtifacts: this.inputArtifacts.map(a => ({ name: a.name })), actionTypeId: { category: this.category.toString(), @@ -174,8 +174,8 @@ export abstract class Action extends cdk.Construct { source: [ 'aws.codepipeline' ], resources: [ this.stage.pipeline.pipelineArn ], detail: { - stage: [ this.stage.name ], - action: [ this.name ], + stage: [ this.stage.id ], + action: [ this.id ], }, }); return rule; diff --git a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts index 7b72cbfe7ec01..5520436fac94d 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts @@ -164,7 +164,7 @@ export class Pipeline extends cdk.Construct implements events.IEventRuleTarget { } return { - id: this.name, + id: this.id, arn: this.pipelineArn, roleArn: this.eventsRole.roleArn, }; @@ -224,8 +224,8 @@ export class Pipeline extends cdk.Construct implements events.IEventRuleTarget { } private appendStage(stage: Stage) { - if (this.stages.find(x => x.name === stage.name)) { - throw new Error(`A stage with name '${stage.name}' already exists`); + if (this.stages.find(x => x.id === stage.id)) { + throw new Error(`A stage with name '${stage.id}' already exists`); } this.stages.push(stage); @@ -235,7 +235,7 @@ export class Pipeline extends cdk.Construct implements events.IEventRuleTarget { return util.flatMap(this.stages, (stage, i) => { const onlySourceActionsPermitted = i === 0; return util.flatMap(stage.actions, (action, _) => - validation.validateSourceAction(onlySourceActionsPermitted, action.category, action.name, stage.name) + validation.validateSourceAction(onlySourceActionsPermitted, action.category, action.id, stage.id) ); }); } diff --git a/packages/@aws-cdk/aws-codepipeline/lib/stage.ts b/packages/@aws-cdk/aws-codepipeline/lib/stage.ts index 3f74cf9db9985..65cbc7ddfc541 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/stage.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/stage.ts @@ -46,7 +46,7 @@ export class Stage extends cdk.Construct { public render(): cloudformation.PipelineResource.StageDeclarationProperty { return { - name: this.name, + name: this.id, actions: this._actions.map(action => action.render()) }; } @@ -59,7 +59,7 @@ export class Stage extends cdk.Construct { source: [ 'aws.codepipeline' ], resources: [ this.pipeline.pipelineArn ], detail: { - stage: [ this.name ], + stage: [ this.id ], }, }); return rule; @@ -84,7 +84,7 @@ export class Stage extends cdk.Construct { private validateHasActions(): string[] { if (this._actions.length === 0) { - return [`Stage '${this.name}' must have at least one action`]; + return [`Stage '${this.id}' must have at least one action`]; } return []; } diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts b/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts index 862cacd238e6a..9bf961f808fbf 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts @@ -185,7 +185,7 @@ export abstract class FunctionRef extends cdk.Construct } return { - id: this.name, + id: this.id, arn: this.functionArn, }; } diff --git a/packages/@aws-cdk/aws-sns/lib/topic-ref.ts b/packages/@aws-cdk/aws-sns/lib/topic-ref.ts index c5476d05c4cb9..563915b5cc65c 100644 --- a/packages/@aws-cdk/aws-sns/lib/topic-ref.ts +++ b/packages/@aws-cdk/aws-sns/lib/topic-ref.ts @@ -80,9 +80,9 @@ export abstract class TopicRef extends cdk.Construct implements events.IEventRul * @param queue The target queue */ public subscribeQueue(queue: sqs.QueueRef) { - const subscriptionName = queue.name + 'Subscription'; + const subscriptionName = queue.id + 'Subscription'; if (this.tryFindChild(subscriptionName)) { - throw new Error(`A subscription between the topic ${this.name} and the queue ${queue.name} already exists`); + throw new Error(`A subscription between the topic ${this.id} and the queue ${queue.id} already exists`); } // we use the queue name as the subscription's. there's no meaning to subscribing @@ -114,10 +114,10 @@ export abstract class TopicRef extends cdk.Construct implements events.IEventRul * @param lambdaFunction The Lambda function to invoke */ public subscribeLambda(lambdaFunction: lambda.FunctionRef) { - const subscriptionName = lambdaFunction.name + 'Subscription'; + const subscriptionName = lambdaFunction.id + 'Subscription'; if (this.tryFindChild(subscriptionName)) { - throw new Error(`A subscription between the topic ${this.name} and the lambda ${lambdaFunction.name} already exists`); + throw new Error(`A subscription between the topic ${this.id} and the lambda ${lambdaFunction.id} already exists`); } const sub = new Subscription(this, subscriptionName, { @@ -126,7 +126,7 @@ export abstract class TopicRef extends cdk.Construct implements events.IEventRul protocol: SubscriptionProtocol.Lambda }); - lambdaFunction.addPermission(this.name, { + lambdaFunction.addPermission(this.id, { sourceArn: this.topicArn, principal: new cdk.ServicePrincipal('sns.amazonaws.com'), }); @@ -223,7 +223,7 @@ export abstract class TopicRef extends cdk.Construct implements events.IEventRul } return { - id: this.name, + id: this.id, arn: this.topicArn, }; } diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/logical-id.ts b/packages/@aws-cdk/cdk/lib/cloudformation/logical-id.ts index 5f89093795981..b1a30a40b84d7 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/logical-id.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/logical-id.ts @@ -1,5 +1,5 @@ import { makeUniqueId } from '../util/uniqueid'; -import { StackElement } from "./stack"; +import { StackElement } from './stack'; const PATH_SEP = '/'; diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts b/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts index 9afe7265bdec4..9f1b4a799a8fb 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts @@ -86,10 +86,16 @@ export class Stack extends Construct { */ public readonly templateOptions: TemplateOptions = {}; + /** + * The CloudFormation stack name. + */ + public readonly name: string; + /** * Creates a new stack. * * @param parent Parent of this stack, usually a Program instance. + * @param name The name of the CloudFormation stack. Defaults to "Stack". * @param props Stack properties. */ public constructor(parent?: App, name?: string, props?: StackProps) { @@ -98,6 +104,7 @@ export class Stack extends Construct { this.env = this.parseEnvironment(props); this.logicalIds = new LogicalIDs(props && props.namingScheme ? props.namingScheme : new HashedAddressingScheme()); + this.name = name || 'Stack'; } /** @@ -195,7 +202,7 @@ export class Stack extends Construct { * CloudFormation stack names can include dashes in addition to the regular identifier * character classes, and we don't allow one of the magic markers. */ - protected _validateName(name: string) { + protected _validateId(name: string) { if (!Stack.VALID_STACK_NAME_REGEX.test(name)) { throw new Error(`Stack name must match the regular expression: ${Stack.VALID_STACK_NAME_REGEX.toString()}, got '${name}'`); } diff --git a/packages/@aws-cdk/cdk/lib/core/construct.ts b/packages/@aws-cdk/cdk/lib/core/construct.ts index 3b52c7092b7d1..16fd813a89130 100644 --- a/packages/@aws-cdk/cdk/lib/core/construct.ts +++ b/packages/@aws-cdk/cdk/lib/core/construct.ts @@ -13,13 +13,9 @@ export class Construct { public readonly parent?: Construct; /** - * @deprecated use `id` - */ - public readonly name: string; - - /** - * The subtree-local id of the construct. - * This id is unique within the subtree. To obtain a tree-unique id, use `uniqueId`. + * The local id of the construct. + * This id is unique amongst its siblings. + * To obtain a tree-global unique id for this construct, use `uniqueId`. */ public readonly id: string; @@ -30,7 +26,7 @@ export class Construct { public readonly path: string; /** - * A tree-unique alpha-numeric identifier for this construct. + * A tree-global unique alphanumeric identifier for this construct. * Includes all components of the tree. */ public readonly uniqueId: string; @@ -56,7 +52,6 @@ export class Construct { */ constructor(parent: Construct, id: string) { this.id = id; - this.name = id; // legacy this.parent = parent; // We say that parent is required, but some root constructs bypass the type checks and @@ -75,7 +70,7 @@ export class Construct { // Validate the name we ended up with if (this.id !== '') { - this._validateName(this.id); + this._validateId(this.id); } const components = this.rootPath().map(c => c.id); @@ -294,12 +289,12 @@ export class Construct { } /** - * Validate that the name of the construct is a legal identifier. - * Construct names can be any characters besides the path separator. + * Validate that the id of the construct legal. + * Construct IDs can be any characters besides the path separator. */ - protected _validateName(name: string) { - if (name.indexOf(PATH_SEP) !== -1) { - throw new Error(`Construct names cannot include '${PATH_SEP}': ${name}`); + protected _validateId(id: string) { + if (id.indexOf(PATH_SEP) !== -1) { + throw new Error(`Construct names cannot include '${PATH_SEP}': ${id}`); } } diff --git a/packages/@aws-cdk/cdk/lib/util/.gitignore b/packages/@aws-cdk/cdk/lib/util/.gitignore deleted file mode 100644 index f3e1fe1de16f9..0000000000000 --- a/packages/@aws-cdk/cdk/lib/util/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -!md5.js - diff --git a/packages/@aws-cdk/cdk/lib/util/md5.js b/packages/@aws-cdk/cdk/lib/util/md5.js deleted file mode 100644 index 762de3c52012a..0000000000000 --- a/packages/@aws-cdk/cdk/lib/util/md5.js +++ /dev/null @@ -1,280 +0,0 @@ -/* - * JavaScript MD5 - * https://github.com/blueimp/JavaScript-MD5 - * - * Copyright 2011, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * https://opensource.org/licenses/MIT - * - * Based on - * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message - * Digest Algorithm, as defined in RFC 1321. - * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * Distributed under the BSD License - * See http://pajhome.org.uk/crypt/md5 for more info. - */ - -/* global define */ - -;(function ($) { - 'use strict' - - /* - * Add integers, wrapping at 2^32. This uses 16-bit operations internally - * to work around bugs in some JS interpreters. - */ - function safeAdd (x, y) { - var lsw = (x & 0xffff) + (y & 0xffff) - var msw = (x >> 16) + (y >> 16) + (lsw >> 16) - return (msw << 16) | (lsw & 0xffff) - } - - /* - * Bitwise rotate a 32-bit number to the left. - */ - function bitRotateLeft (num, cnt) { - return (num << cnt) | (num >>> (32 - cnt)) - } - - /* - * These functions implement the four basic operations the algorithm uses. - */ - function md5cmn (q, a, b, x, s, t) { - return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b) - } - function md5ff (a, b, c, d, x, s, t) { - return md5cmn((b & c) | (~b & d), a, b, x, s, t) - } - function md5gg (a, b, c, d, x, s, t) { - return md5cmn((b & d) | (c & ~d), a, b, x, s, t) - } - function md5hh (a, b, c, d, x, s, t) { - return md5cmn(b ^ c ^ d, a, b, x, s, t) - } - function md5ii (a, b, c, d, x, s, t) { - return md5cmn(c ^ (b | ~d), a, b, x, s, t) - } - - /* - * Calculate the MD5 of an array of little-endian words, and a bit length. - */ - function binlMD5 (x, len) { - /* append padding */ - x[len >> 5] |= 0x80 << (len % 32) - x[((len + 64) >>> 9 << 4) + 14] = len - - var i - var olda - var oldb - var oldc - var oldd - var a = 1732584193 - var b = -271733879 - var c = -1732584194 - var d = 271733878 - - for (i = 0; i < x.length; i += 16) { - olda = a - oldb = b - oldc = c - oldd = d - - a = md5ff(a, b, c, d, x[i], 7, -680876936) - d = md5ff(d, a, b, c, x[i + 1], 12, -389564586) - c = md5ff(c, d, a, b, x[i + 2], 17, 606105819) - b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330) - a = md5ff(a, b, c, d, x[i + 4], 7, -176418897) - d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426) - c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341) - b = md5ff(b, c, d, a, x[i + 7], 22, -45705983) - a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416) - d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417) - c = md5ff(c, d, a, b, x[i + 10], 17, -42063) - b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162) - a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682) - d = md5ff(d, a, b, c, x[i + 13], 12, -40341101) - c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290) - b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329) - - a = md5gg(a, b, c, d, x[i + 1], 5, -165796510) - d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632) - c = md5gg(c, d, a, b, x[i + 11], 14, 643717713) - b = md5gg(b, c, d, a, x[i], 20, -373897302) - a = md5gg(a, b, c, d, x[i + 5], 5, -701558691) - d = md5gg(d, a, b, c, x[i + 10], 9, 38016083) - c = md5gg(c, d, a, b, x[i + 15], 14, -660478335) - b = md5gg(b, c, d, a, x[i + 4], 20, -405537848) - a = md5gg(a, b, c, d, x[i + 9], 5, 568446438) - d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690) - c = md5gg(c, d, a, b, x[i + 3], 14, -187363961) - b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501) - a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467) - d = md5gg(d, a, b, c, x[i + 2], 9, -51403784) - c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473) - b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734) - - a = md5hh(a, b, c, d, x[i + 5], 4, -378558) - d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463) - c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562) - b = md5hh(b, c, d, a, x[i + 14], 23, -35309556) - a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060) - d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353) - c = md5hh(c, d, a, b, x[i + 7], 16, -155497632) - b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640) - a = md5hh(a, b, c, d, x[i + 13], 4, 681279174) - d = md5hh(d, a, b, c, x[i], 11, -358537222) - c = md5hh(c, d, a, b, x[i + 3], 16, -722521979) - b = md5hh(b, c, d, a, x[i + 6], 23, 76029189) - a = md5hh(a, b, c, d, x[i + 9], 4, -640364487) - d = md5hh(d, a, b, c, x[i + 12], 11, -421815835) - c = md5hh(c, d, a, b, x[i + 15], 16, 530742520) - b = md5hh(b, c, d, a, x[i + 2], 23, -995338651) - - a = md5ii(a, b, c, d, x[i], 6, -198630844) - d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415) - c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905) - b = md5ii(b, c, d, a, x[i + 5], 21, -57434055) - a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571) - d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606) - c = md5ii(c, d, a, b, x[i + 10], 15, -1051523) - b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799) - a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359) - d = md5ii(d, a, b, c, x[i + 15], 10, -30611744) - c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380) - b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649) - a = md5ii(a, b, c, d, x[i + 4], 6, -145523070) - d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379) - c = md5ii(c, d, a, b, x[i + 2], 15, 718787259) - b = md5ii(b, c, d, a, x[i + 9], 21, -343485551) - - a = safeAdd(a, olda) - b = safeAdd(b, oldb) - c = safeAdd(c, oldc) - d = safeAdd(d, oldd) - } - return [a, b, c, d] - } - - /* - * Convert an array of little-endian words to a string - */ - function binl2rstr (input) { - var i - var output = '' - var length32 = input.length * 32 - for (i = 0; i < length32; i += 8) { - output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff) - } - return output - } - - /* - * Convert a raw string to an array of little-endian words - * Characters >255 have their high-byte silently ignored. - */ - function rstr2binl (input) { - var i - var output = [] - output[(input.length >> 2) - 1] = undefined - for (i = 0; i < output.length; i += 1) { - output[i] = 0 - } - var length8 = input.length * 8 - for (i = 0; i < length8; i += 8) { - output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32) - } - return output - } - - /* - * Calculate the MD5 of a raw string - */ - function rstrMD5 (s) { - return binl2rstr(binlMD5(rstr2binl(s), s.length * 8)) - } - - /* - * Calculate the HMAC-MD5, of a key and some data (raw strings) - */ - function rstrHMACMD5 (key, data) { - var i - var bkey = rstr2binl(key) - var ipad = [] - var opad = [] - var hash - ipad[15] = opad[15] = undefined - if (bkey.length > 16) { - bkey = binlMD5(bkey, key.length * 8) - } - for (i = 0; i < 16; i += 1) { - ipad[i] = bkey[i] ^ 0x36363636 - opad[i] = bkey[i] ^ 0x5c5c5c5c - } - hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8) - return binl2rstr(binlMD5(opad.concat(hash), 512 + 128)) - } - - /* - * Convert a raw string to a hex string - */ - function rstr2hex (input) { - var hexTab = '0123456789abcdef' - var output = '' - var x - var i - for (i = 0; i < input.length; i += 1) { - x = input.charCodeAt(i) - output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f) - } - return output - } - - /* - * Encode a string as utf-8 - */ - function str2rstrUTF8 (input) { - return unescape(encodeURIComponent(input)) - } - - /* - * Take string arguments and return either raw or hex encoded strings - */ - function rawMD5 (s) { - return rstrMD5(str2rstrUTF8(s)) - } - function hexMD5 (s) { - return rstr2hex(rawMD5(s)) - } - function rawHMACMD5 (k, d) { - return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d)) - } - function hexHMACMD5 (k, d) { - return rstr2hex(rawHMACMD5(k, d)) - } - - function md5 (string, key, raw) { - if (!key) { - if (!raw) { - return hexMD5(string) - } - return rawMD5(string) - } - if (!raw) { - return hexHMACMD5(key, string) - } - return rawHMACMD5(key, string) - } - - if (typeof define === 'function' && define.amd) { - define(function () { - return md5 - }) - } else if (typeof module === 'object' && module.exports) { - module.exports = md5 - } else { - $.md5 = md5 - } -})(this) diff --git a/packages/@aws-cdk/cdk/lib/util/uniqueid.ts b/packages/@aws-cdk/cdk/lib/util/uniqueid.ts index 6c7e27a87ebf9..43edf7286c47f 100644 --- a/packages/@aws-cdk/cdk/lib/util/uniqueid.ts +++ b/packages/@aws-cdk/cdk/lib/util/uniqueid.ts @@ -1,5 +1,5 @@ // tslint:disable-next-line:no-var-requires -const md5 = require('./md5'); +import crypto = require('crypto'); /** * Resources with this ID are hidden from humans @@ -20,17 +20,19 @@ const HASH_LEN = 8; const MAX_HUMAN_LEN = 240; // max ID len is 255 /** - * Given a set of named path components, returns a unique alpha-numeric identifier - * with a maximum length of 255. This is done by calculating a hash on the full path - * and using it as a suffix of a length-limited "human" rendition of the path components. + * Calculates a unique ID for a set of textual components. + * + * This is done by calculating a hash on the full path and using it as a suffix + * of a length-limited "human" rendition of the path components. * * @param components The path components + * @returns a unique alpha-numeric identifier with a maximum length of 255 */ export function makeUniqueId(components: string[]) { components = components.filter(x => x !== HIDDEN_ID); if (components.length === 0) { - throw new Error('Unable to calculate a unique ID for an empty path'); + throw new Error('Unable to calculate a unique id for an empty set of components'); } // top-level resources will simply use the `name` as-is in order to support @@ -42,8 +44,8 @@ export function makeUniqueId(components: string[]) { const hash = pathHash(components); const human = removeDupes(components) - .map(removeNonAlpha) .filter(x => x !== HIDDEN_FROM_HUMAN_ID) + .map(removeNonAlphanumeric) .join('') .slice(0, MAX_HUMAN_LEN); @@ -56,21 +58,22 @@ export function makeUniqueId(components: string[]) { * The hash is limited in size. */ function pathHash(path: string[]): string { - return md5(path.join(PATH_SEP)).slice(0, HASH_LEN).toUpperCase(); + const md5 = crypto.createHash('md5').update(path.join(PATH_SEP)).digest("hex"); + return md5.slice(0, HASH_LEN).toUpperCase(); } /** * Removes all non-alphanumeric characters in a string. */ -function removeNonAlpha(s: string) { +function removeNonAlphanumeric(s: string) { return s.replace(/[^A-Za-z0-9]/g, ''); } /** * Remove duplicate "terms" from the path list * - * If a component name is completely the same as the suffix of - * the previous component name, we get rid of it. + * If the previous path component name ends with this component name, skip the + * current component. */ function removeDupes(path: string[]): string[] { const ret = new Array(); diff --git a/packages/@aws-cdk/cdk/test/core/test.construct.ts b/packages/@aws-cdk/cdk/test/core/test.construct.ts index 70d4c80577d59..c7a563beb2d4b 100644 --- a/packages/@aws-cdk/cdk/test/core/test.construct.ts +++ b/packages/@aws-cdk/cdk/test/core/test.construct.ts @@ -67,6 +67,12 @@ export = { test.done(); }, + 'cannot calculate uniqueId if the construct path is ["Default"]'(test: Test) { + const root = new Root(); + test.throws(() => new Construct(root, 'Default'), /Unable to calculate a unique id for an empty set of components/); + test.done(); + }, + 'construct.getChildren() returns an array of all children'(test: Test) { const root = new Root(); const child = new Construct(root, 'Child1'); @@ -424,7 +430,7 @@ class ConstructWithRequired extends Construct { * Construct that *must* be named "Spartacus" */ class IAmSpartacusConstruct extends Construct { - protected _validateName(name: string) { + protected _validateId(name: string) { if (name !== "Spartacus") { throw new Error("Construct name must be 'Spartacus'"); } From 96994285ae99f45708b4d4b96b376576918c7237 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Tue, 14 Aug 2018 20:00:47 +0300 Subject: [PATCH 16/33] feat(aws-sqs): support bucket notifications (#564) Allow sqs.Queue to be used as a destination for bucket notifications. Also supports queues that are server-side encrypted using a custom KMS key. Added support for specifying KMS keys in `QueueRef`, including export/import support. Added support to `kms.EncryptionKey` to allow forbidding "no-op" when adding policy statements. This is needed in order to fail the user from subscribing a queue to receive bucket notifications without the ability to allow S3 to read the confirmation messages from the queue as described in the S3 documentation [1] [1]: https://docs.aws.amazon.com/AmazonS3/latest/dev/ways-to-add-notification-config-to-bucket.html --- packages/@aws-cdk/aws-kms/lib/key.ts | 11 +- packages/@aws-cdk/aws-kms/test/test.key.ts | 28 +- packages/@aws-cdk/aws-sqs/lib/queue-ref.ts | 85 ++++- packages/@aws-cdk/aws-sqs/lib/queue.ts | 19 +- packages/@aws-cdk/aws-sqs/package.json | 4 +- .../integ.bucket-notifications.expected.json | 341 ++++++++++++++++++ .../test/integ.bucket-notifications.ts | 20 + packages/@aws-cdk/aws-sqs/test/test.sqs.ts | 321 ++++++++++++++++- 8 files changed, 799 insertions(+), 30 deletions(-) create mode 100644 packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.expected.json create mode 100644 packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.ts diff --git a/packages/@aws-cdk/aws-kms/lib/key.ts b/packages/@aws-cdk/aws-kms/lib/key.ts index 16578888c19fc..cf98224910cca 100644 --- a/packages/@aws-cdk/aws-kms/lib/key.ts +++ b/packages/@aws-cdk/aws-kms/lib/key.ts @@ -1,4 +1,4 @@ -import { Construct, DeletionPolicy, Output, PolicyDocument, PolicyStatement } from '@aws-cdk/cdk'; +import { Construct, DeletionPolicy, Output, PolicyDocument, PolicyStatement, resolve } from '@aws-cdk/cdk'; import { EncryptionKeyAlias } from './alias'; import { cloudformation, KeyArn } from './kms.generated'; @@ -54,10 +54,15 @@ export abstract class EncryptionKeyRef extends Construct { /** * Adds a statement to the KMS key resource policy. + * @param statement The policy statement to add + * @param allowNoOp If this is set to `false` and there is no policy + * defined (i.e. external key), the operation will fail. Otherwise, it will + * no-op. */ - public addToResourcePolicy(statement: PolicyStatement) { + public addToResourcePolicy(statement: PolicyStatement, allowNoOp = true) { if (!this.policy) { - return; + if (allowNoOp) { return; } + throw new Error(`Unable to add statement to IAM resource policy for KMS key: ${JSON.stringify(resolve(this.keyArn))}`); } this.policy.addStatement(statement); diff --git a/packages/@aws-cdk/aws-kms/test/test.key.ts b/packages/@aws-cdk/aws-kms/test/test.key.ts index 3fcfe53410801..e66b20b2e2240 100644 --- a/packages/@aws-cdk/aws-kms/test/test.key.ts +++ b/packages/@aws-cdk/aws-kms/test/test.key.ts @@ -1,7 +1,7 @@ import { exactlyMatchTemplate, expect } from '@aws-cdk/assert'; import { App, PolicyDocument, PolicyStatement, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; -import { EncryptionKey } from '../lib'; +import { EncryptionKey, KeyArn } from '../lib'; export = { 'default key'(test: Test) { @@ -349,5 +349,31 @@ export = { }); test.done(); + }, + + 'addToResourcePolicy allowNoOp and there is no policy': { + 'succeed if set to true (default)'(test: Test) { + const stack = new Stack(); + + const key = EncryptionKey.import(stack, 'Imported', { keyArn: new KeyArn('foo/bar') }); + + key.addToResourcePolicy(new PolicyStatement().addResource('*').addAction('*')); + + test.done(); + }, + + 'fails if set to false'(test: Test) { + + const stack = new Stack(); + + const key = EncryptionKey.import(stack, 'Imported', { keyArn: new KeyArn('foo/bar') }); + + test.throws(() => + key.addToResourcePolicy(new PolicyStatement().addResource('*').addAction('*'), /* allowNoOp */ false), + 'Unable to add statement to IAM resource policy for KMS key: "foo/bar"'); + + test.done(); + + } } }; diff --git a/packages/@aws-cdk/aws-sqs/lib/queue-ref.ts b/packages/@aws-cdk/aws-sqs/lib/queue-ref.ts index c6132cbcf4e48..df9493566fd3f 100644 --- a/packages/@aws-cdk/aws-sqs/lib/queue-ref.ts +++ b/packages/@aws-cdk/aws-sqs/lib/queue-ref.ts @@ -1,15 +1,17 @@ -import { Construct, Output, PolicyStatement, Token } from '@aws-cdk/cdk'; +import kms = require('@aws-cdk/aws-kms'); +import s3n = require('@aws-cdk/aws-s3-notifications'); +import cdk = require('@aws-cdk/cdk'); import { QueuePolicy } from './policy'; import { QueueArn } from './sqs.generated'; /** * Reference to a new or existing Amazon SQS queue */ -export abstract class QueueRef extends Construct { +export abstract class QueueRef extends cdk.Construct implements s3n.IBucketNotificationDestination { /** * Import an existing queue */ - public static import(parent: Construct, name: string, props: QueueRefProps) { + public static import(parent: cdk.Construct, name: string, props: QueueRefProps) { new ImportedQueue(parent, name, props); } @@ -23,6 +25,11 @@ export abstract class QueueRef extends Construct { */ public abstract readonly queueUrl: QueueUrl; + /** + * If this queue is server-side encrypted, this is the KMS encryption key. + */ + public abstract readonly encryptionMasterKey?: kms.EncryptionKeyRef; + /** * Controls automatic creation of policy objects. * @@ -32,13 +39,21 @@ export abstract class QueueRef extends Construct { private policy?: QueuePolicy; + /** + * The set of S3 bucket IDs that are allowed to send notifications to this queue. + */ + private readonly notifyingBuckets = new Set(); + /** * Export a queue */ public export(): QueueRefProps { return { - queueArn: new Output(this, 'QueueArn', { value: this.queueArn }).makeImportValue(), - queueUrl: new Output(this, 'QueueUrl', { value: this.queueUrl }).makeImportValue(), + queueArn: new cdk.Output(this, 'QueueArn', { value: this.queueArn }).makeImportValue(), + queueUrl: new cdk.Output(this, 'QueueUrl', { value: this.queueUrl }).makeImportValue(), + keyArn: this.encryptionMasterKey + ? new cdk.Output(this, 'KeyArn', { value: this.encryptionMasterKey.keyArn }).makeImportValue() + : undefined }; } @@ -49,7 +64,7 @@ export abstract class QueueRef extends Construct { * will be automatically created upon the first call to `addToPolicy`. If * the queue is improted (`Queue.import`), then this is a no-op. */ - public addToResourcePolicy(statement: PolicyStatement) { + public addToResourcePolicy(statement: cdk.PolicyStatement) { if (!this.policy && this.autoCreatePolicy) { this.policy = new QueuePolicy(this, 'Policy', { queues: [ this ] }); } @@ -59,14 +74,61 @@ export abstract class QueueRef extends Construct { } } + /** + * Allows using SQS queues as destinations for bucket notifications. + * Use `bucket.onEvent(event, queue)` to subscribe. + * @param bucketArn The ARN of the notifying bucket. + * @param bucketId A unique ID for the notifying bucket. + */ + public asBucketNotificationDestination(bucketArn: cdk.Arn, bucketId: string): s3n.BucketNotificationDestinationProps { + if (!this.notifyingBuckets.has(bucketId)) { + this.addToResourcePolicy(new cdk.PolicyStatement() + .addServicePrincipal('s3.amazonaws.com') + .addAction('sqs:SendMessage') + .addResource(this.queueArn) + .addCondition('ArnLike', { 'aws:SourceArn': bucketArn })); + + // if this queue is encrypted, we need to allow S3 to read messages since that's how + // it verifies that the notification destination configuration is valid. + // by setting allowNoOp to false, we ensure that only custom keys that we can actually + // control access to can be used here as described in: + // https://docs.aws.amazon.com/AmazonS3/latest/dev/ways-to-add-notification-config-to-bucket.html + if (this.encryptionMasterKey) { + this.encryptionMasterKey.addToResourcePolicy(new cdk.PolicyStatement() + .addServicePrincipal('s3.amazonaws.com') + .addAction('kms:GenerateDataKey') + .addAction('kms:Decrypt') + .addResource('*'), /* allowNoOp */ false); + } + + this.notifyingBuckets.add(bucketId); + } + + return { + arn: this.queueArn, + type: s3n.BucketNotificationDestinationType.Queue + }; + } } /** * Reference to a queue */ export interface QueueRefProps { + /** + * The ARN of the queue. + */ queueArn: QueueArn; + + /** + * The URL of the queue. + */ queueUrl: QueueUrl; + + /** + * KMS encryption key, if this queue is server-side encrypted by a KMS key. + */ + keyArn?: kms.KeyArn; } /** @@ -75,18 +137,25 @@ export interface QueueRefProps { class ImportedQueue extends QueueRef { public readonly queueArn: QueueArn; public readonly queueUrl: QueueUrl; + public readonly encryptionMasterKey?: kms.EncryptionKeyRef; protected readonly autoCreatePolicy = false; - constructor(parent: Construct, name: string, props: QueueRefProps) { + constructor(parent: cdk.Construct, name: string, props: QueueRefProps) { super(parent, name); this.queueArn = props.queueArn; this.queueUrl = props.queueUrl; + + if (props.keyArn) { + this.encryptionMasterKey = kms.EncryptionKey.import(this, 'Key', { + keyArn: props.keyArn + }); + } } } /** * URL of a queue */ -export class QueueUrl extends Token { +export class QueueUrl extends cdk.Token { } diff --git a/packages/@aws-cdk/aws-sqs/lib/queue.ts b/packages/@aws-cdk/aws-sqs/lib/queue.ts index 27541aca40509..e301133f389be 100644 --- a/packages/@aws-cdk/aws-sqs/lib/queue.ts +++ b/packages/@aws-cdk/aws-sqs/lib/queue.ts @@ -195,6 +195,11 @@ export class Queue extends QueueRef { */ public readonly queueUrl: QueueUrl; + /** + * If this queue is encrypted, this is the KMS key. + */ + public encryptionMasterKey?: kms.EncryptionKeyRef; + protected readonly autoCreatePolicy = true; constructor(parent: cdk.Construct, name: string, props: QueueProps = {}) { @@ -255,10 +260,10 @@ export class Queue extends QueueRef { } private determineEncryptionProps(props: QueueProps): EncryptionProps { - const encryption = props.encryption || QueueEncryption.Unencrypted; + let encryption = props.encryption || QueueEncryption.Unencrypted; if (encryption !== QueueEncryption.Kms && props.encryptionMasterKey) { - throw new Error("Encryption key is specified, so 'encryption' must be set to 'Kms'."); + encryption = QueueEncryption.Kms; // KMS is implied by specifying an encryption key } if (encryption === QueueEncryption.Unencrypted) { @@ -266,19 +271,23 @@ export class Queue extends QueueRef { } if (encryption === QueueEncryption.KmsManaged) { + this.encryptionMasterKey = kms.EncryptionKey.import(this, 'Key', { + keyArn: new kms.KeyArn('alias/aws/sqs') + }); + return { kmsMasterKeyId: 'alias/aws/sqs', kmsDataKeyReusePeriodSeconds: props.dataKeyReuseSec }; } - if (props.encryption === QueueEncryption.Kms) { - const encryptionKey = props.encryptionMasterKey || new kms.EncryptionKey(this, 'Key', { + if (encryption === QueueEncryption.Kms) { + this.encryptionMasterKey = props.encryptionMasterKey || new kms.EncryptionKey(this, 'Key', { description: `Created by ${this.path}` }); return { - kmsMasterKeyId: encryptionKey.keyArn, + kmsMasterKeyId: this.encryptionMasterKey.keyArn, kmsDataKeyReusePeriodSeconds: props.dataKeyReuseSec }; } diff --git a/packages/@aws-cdk/aws-sqs/package.json b/packages/@aws-cdk/aws-sqs/package.json index c58a481d4d47e..73b1b18956fa5 100644 --- a/packages/@aws-cdk/aws-sqs/package.json +++ b/packages/@aws-cdk/aws-sqs/package.json @@ -51,10 +51,12 @@ "cdk-build-tools": "^0.8.1", "cdk-integ-tools": "^0.8.1", "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "pkglint": "^0.8.1", + "@aws-cdk/aws-s3": "^0.8.1" }, "dependencies": { "@aws-cdk/aws-kms": "^0.8.1", + "@aws-cdk/aws-s3-notifications": "^0.8.1", "@aws-cdk/cdk": "^0.8.1" }, "homepage": "https://github.com/awslabs/aws-cdk" diff --git a/packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.expected.json b/packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.expected.json new file mode 100644 index 0000000000000..93adc81f02f79 --- /dev/null +++ b/packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.expected.json @@ -0,0 +1,341 @@ +{ + "Resources": { + "Bucket12520700A": { + "Type": "AWS::S3::Bucket" + }, + "Bucket1NotificationsBC5D9A45": { + "Type": "Custom::S3BucketNotifications", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", + "Arn" + ] + }, + "BucketName": { + "Ref": "Bucket12520700A" + }, + "NotificationConfiguration": { + "QueueConfigurations": [ + { + "Events": [ + "s3:ObjectCreated:*" + ], + "QueueArn": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + } + }, + { + "Events": [ + "s3:ObjectRemoved:*" + ], + "QueueArn": { + "Fn::GetAtt": [ + "EncryptedQueue0428C61A", + "Arn" + ] + } + } + ] + } + } + }, + "MyQueueE6CA6235": { + "Type": "AWS::SQS::Queue" + }, + "MyQueuePolicy6BBEDDAC": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "Bucket12520700A", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "s3.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + } + }, + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "Bucket25524B414", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "s3.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "MyQueueE6CA6235" + } + ] + } + }, + "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn", + ":", + { + "Ref": "AWS::Partition" + }, + ":", + "iam", + ":", + "", + ":", + "aws", + ":", + "policy", + "/", + "service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleDefaultPolicy2CF63D36": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutBucketNotification", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleDefaultPolicy2CF63D36", + "Roles": [ + { + "Ref": "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC" + } + ] + } + }, + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)", + "Code": { + "ZipFile": "exports.handler = (event, context) => {\n const s3 = new (require('aws-sdk').S3)();\n const https = require(\"https\");\n const url = require(\"url\");\n log(JSON.stringify(event, undefined, 2));\n const props = event.ResourceProperties;\n if (event.RequestType === 'Delete') {\n props.NotificationConfiguration = {}; // this is how you clean out notifications\n }\n const req = {\n Bucket: props.BucketName,\n NotificationConfiguration: props.NotificationConfiguration\n };\n return s3.putBucketNotificationConfiguration(req, (err, data) => {\n log({ err, data });\n if (err) {\n return submitResponse(\"FAILED\", err.message + `\\nMore information in CloudWatch Log Stream: ${context.logStreamName}`);\n }\n else {\n return submitResponse(\"SUCCESS\");\n }\n });\n function log(obj) {\n console.error(event.RequestId, event.StackId, event.LogicalResourceId, obj);\n }\n // tslint:disable-next-line:max-line-length\n // adapted from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule\n // to allow sending an error messge as a reason.\n function submitResponse(responseStatus, reason) {\n const responseBody = JSON.stringify({\n Status: responseStatus,\n Reason: reason || \"See the details in CloudWatch Log Stream: \" + context.logStreamName,\n PhysicalResourceId: context.logStreamName,\n StackId: event.StackId,\n RequestId: event.RequestId,\n LogicalResourceId: event.LogicalResourceId,\n NoEcho: false,\n });\n log({ responseBody });\n const parsedUrl = url.parse(event.ResponseURL);\n const options = {\n hostname: parsedUrl.hostname,\n port: 443,\n path: parsedUrl.path,\n method: \"PUT\",\n headers: {\n \"content-type\": \"\",\n \"content-length\": responseBody.length\n }\n };\n const request = https.request(options, (r) => {\n log({ statusCode: r.statusCode, statusMessage: r.statusMessage });\n context.done();\n });\n request.on(\"error\", (error) => {\n log({ sendError: error });\n context.done();\n });\n request.write(responseBody);\n request.end();\n }\n};" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Timeout": 300 + } + }, + "Bucket25524B414": { + "Type": "AWS::S3::Bucket" + }, + "Bucket2NotificationsD9BA2A77": { + "Type": "Custom::S3BucketNotifications", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691", + "Arn" + ] + }, + "BucketName": { + "Ref": "Bucket25524B414" + }, + "NotificationConfiguration": { + "QueueConfigurations": [ + { + "Events": [ + "s3:ObjectCreated:*" + ], + "Filter": { + "Key": { + "FilterRules": [ + { + "Name": "suffix", + "Value": ".png" + } + ] + } + }, + "QueueArn": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + } + } + ] + } + } + }, + "EncryptedQueueKey6F4FD304": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:GenerateDataKey", + "kms:Decrypt" + ], + "Effect": "Allow", + "Principal": { + "Service": "s3.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "Description": "Created by sqs-bucket-notifications/EncryptedQueue" + }, + "DeletionPolicy": "Retain" + }, + "EncryptedQueue0428C61A": { + "Type": "AWS::SQS::Queue", + "Properties": { + "KmsMasterKeyId": { + "Fn::GetAtt": [ + "EncryptedQueueKey6F4FD304", + "Arn" + ] + } + } + }, + "EncryptedQueuePolicy8AEB1708": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "Bucket12520700A", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "s3.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "EncryptedQueue0428C61A", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "EncryptedQueue0428C61A" + } + ] + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.ts b/packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.ts new file mode 100644 index 0000000000000..b8a8dec1f0ef9 --- /dev/null +++ b/packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.ts @@ -0,0 +1,20 @@ +import s3 = require('@aws-cdk/aws-s3'); +import cdk = require('@aws-cdk/cdk'); +import sqs = require('../lib'); + +const app = new cdk.App(process.argv); + +const stack = new cdk.Stack(app, 'sqs-bucket-notifications'); + +const bucket1 = new s3.Bucket(stack, 'Bucket1'); +const queue = new sqs.Queue(stack, 'MyQueue'); + +bucket1.onObjectCreated(queue); + +const bucket2 = new s3.Bucket(stack, 'Bucket2'); +bucket2.onObjectCreated(queue, { suffix: '.png' }); + +const encryptedQueue = new sqs.Queue(stack, 'EncryptedQueue', { encryption: sqs.QueueEncryption.Kms }); +bucket1.onObjectRemoved(encryptedQueue); + +process.stdout.write(app.run()); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sqs/test/test.sqs.ts b/packages/@aws-cdk/aws-sqs/test/test.sqs.ts index b22090986dcdd..00aca29685afc 100644 --- a/packages/@aws-cdk/aws-sqs/test/test.sqs.ts +++ b/packages/@aws-cdk/aws-sqs/test/test.sqs.ts @@ -1,14 +1,16 @@ -import { expect } from '@aws-cdk/assert'; -import { ArnPrincipal, PolicyStatement, Stack } from '@aws-cdk/cdk'; +import { expect, haveResource } from '@aws-cdk/assert'; +import kms = require('@aws-cdk/aws-kms'); +import s3 = require('@aws-cdk/aws-s3'); +import { ArnPrincipal, PolicyStatement, resolve, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; -import { Queue, QueueRef } from '../lib'; +import sqs = require('../lib'); // tslint:disable:object-literal-key-quotes export = { 'default properties'(test: Test) { const stack = new Stack(); - new Queue(stack, 'Queue'); + new sqs.Queue(stack, 'Queue'); expect(stack).toMatch({ "Resources": { @@ -22,8 +24,8 @@ export = { }, 'with a dead letter queue'(test: Test) { const stack = new Stack(); - const dlq = new Queue(stack, 'DLQ'); - new Queue(stack, 'Queue', { deadLetterQueue: { queue: dlq, maxReceiveCount: 3 } }); + const dlq = new sqs.Queue(stack, 'DLQ'); + new sqs.Queue(stack, 'Queue', { deadLetterQueue: { queue: dlq, maxReceiveCount: 3 } }); expect(stack).toMatch({ "Resources": { @@ -52,7 +54,7 @@ export = { 'addToPolicy will automatically create a policy for this queue'(test: Test) { const stack = new Stack(); - const queue = new Queue(stack, 'MyQueue'); + const queue = new sqs.Queue(stack, 'MyQueue'); queue.addToResourcePolicy(new PolicyStatement().addResource('*').addActions('sqs:*').addPrincipal(new ArnPrincipal('arn'))); expect(stack).toMatch({ "Resources": { @@ -88,13 +90,308 @@ export = { }, 'exporting and importing works'(test: Test) { - const stack = new Stack(); - const queue = new Queue(stack, 'Queue'); + const stack = new Stack(); + const queue = new sqs.Queue(stack, 'Queue'); + + const ref = queue.export(); + + sqs.QueueRef.import(stack, 'Imported', ref); + + test.done(); + }, + + 'queue encryption': { + 'encryptionMasterKey can be set to a custom KMS key'(test: Test) { + const stack = new Stack(); + + const key = new kms.EncryptionKey(stack, 'CustomKey'); + const queue = new sqs.Queue(stack, 'Queue', { encryptionMasterKey: key }); + + test.same(queue.encryptionMasterKey, key); + expect(stack).to(haveResource('AWS::SQS::Queue', { + "KmsMasterKeyId": { "Fn::GetAtt": [ "CustomKey1E6D0D07", "Arn" ] } + })); + + test.done(); + }, + + 'a kms key will be allocated if encryption = kms but a master key is not specified'(test: Test) { + const stack = new Stack(); + + new sqs.Queue(stack, 'Queue', { encryption: sqs.QueueEncryption.Kms }); + + expect(stack).to(haveResource('AWS::KMS::Key')); + expect(stack).to(haveResource('AWS::SQS::Queue', { + "KmsMasterKeyId": { + "Fn::GetAtt": [ + "QueueKey39FCBAE6", + "Arn" + ] + } + })); + + test.done(); + }, + + 'it is possible to use a managed kms key'(test: Test) { + const stack = new Stack(); + + new sqs.Queue(stack, 'Queue', { encryption: sqs.QueueEncryption.KmsManaged }); + expect(stack).toMatch({ + "Resources": { + "Queue4A7E3555": { + "Type": "AWS::SQS::Queue", + "Properties": { + "KmsMasterKeyId": "alias/aws/sqs" + } + } + } + }); + test.done(); + }, + + 'export should produce outputs the key arn and return import-values for these outputs': { + + 'with custom key'(test: Test) { + const stack = new Stack(); + + const customKey = new sqs.Queue(stack, 'QueueWithCustomKey', { encryption: sqs.QueueEncryption.Kms }); + + const exportCustom = customKey.export(); + + test.deepEqual(resolve(exportCustom), { + queueArn: { 'Fn::ImportValue': 'QueueWithCustomKeyQueueArnD326BB9B' }, + queueUrl: { 'Fn::ImportValue': 'QueueWithCustomKeyQueueUrlF07DDC70' }, + keyArn: { 'Fn::ImportValue': 'QueueWithCustomKeyKeyArn537F6E42' } + }); + + test.deepEqual(stack.toCloudFormation().Outputs, { + "QueueWithCustomKeyQueueArnD326BB9B": { + "Value": { + "Fn::GetAtt": [ + "QueueWithCustomKeyB3E22087", + "Arn" + ] + }, + "Export": { + "Name": "QueueWithCustomKeyQueueArnD326BB9B" + } + }, + "QueueWithCustomKeyQueueUrlF07DDC70": { + "Value": { + "Ref": "QueueWithCustomKeyB3E22087" + }, + "Export": { + "Name": "QueueWithCustomKeyQueueUrlF07DDC70" + } + }, + "QueueWithCustomKeyKeyArn537F6E42": { + "Value": { + "Fn::GetAtt": [ + "QueueWithCustomKeyD80425F1", + "Arn" + ] + }, + "Export": { + "Name": "QueueWithCustomKeyKeyArn537F6E42" + } + } + }); + test.done(); + }, + + 'with managed key'(test: Test) { + const stack = new Stack(); + + const managedKey = new sqs.Queue(stack, 'QueueWithManagedKey', { encryption: sqs.QueueEncryption.KmsManaged }); + + const exportManaged = managedKey.export(); + + test.deepEqual(resolve(exportManaged), { + queueArn: { 'Fn::ImportValue': 'QueueWithManagedKeyQueueArn8798A14E' }, + queueUrl: { 'Fn::ImportValue': 'QueueWithManagedKeyQueueUrlD735C981' }, + keyArn: { 'Fn::ImportValue': 'QueueWithManagedKeyKeyArn9C42A85D' } + }); + + test.deepEqual(stack.toCloudFormation().Outputs, { + "QueueWithManagedKeyQueueArn8798A14E": { + "Value": { + "Fn::GetAtt": [ + "QueueWithManagedKeyE1B747A1", + "Arn" + ] + }, + "Export": { + "Name": "QueueWithManagedKeyQueueArn8798A14E" + } + }, + "QueueWithManagedKeyQueueUrlD735C981": { + "Value": { + "Ref": "QueueWithManagedKeyE1B747A1" + }, + "Export": { + "Name": "QueueWithManagedKeyQueueUrlD735C981" + } + }, + "QueueWithManagedKeyKeyArn9C42A85D": { + "Value": "alias/aws/sqs", + "Export": { + "Name": "QueueWithManagedKeyKeyArn9C42A85D" + } + } + }); + + test.done(); + } + + } + + }, + + 'bucket notifications': { + + 'queues can be used as destinations'(test: Test) { + const stack = new Stack(); + + const queue = new sqs.Queue(stack, 'Queue'); + const bucket = new s3.Bucket(stack, 'Bucket'); + + bucket.onObjectRemoved(queue); + + expect(stack).to(haveResource('AWS::SQS::QueuePolicy', { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnLike": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "s3.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "Queue4A7E3555", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "Queue4A7E3555" + } + ] + })); + + expect(stack).to(haveResource('Custom::S3BucketNotifications', { + "BucketName": { + "Ref": "Bucket83908E77" + }, + "NotificationConfiguration": { + "QueueConfigurations": [ + { + "Events": [ + "s3:ObjectRemoved:*" + ], + "QueueArn": { + "Fn::GetAtt": [ + "Queue4A7E3555", + "Arn" + ] + } + } + ] + } + })); + + test.done(); + }, + + 'if the queue is encrypted with a custom kms key, the key resource policy is updated to allow s3 to read messages'(test: Test) { + + const stack = new Stack(); + const bucket = new s3.Bucket(stack, 'Bucket'); + const queue = new sqs.Queue(stack, 'Queue', { encryption: sqs.QueueEncryption.Kms }); + + bucket.onObjectCreated(queue); + + expect(stack).to(haveResource('AWS::KMS::Key', { + "KeyPolicy": { + "Statement": [ + { + "Action": [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:GenerateDataKey", + "kms:Decrypt" + ], + "Effect": "Allow", + "Principal": { + "Service": "s3.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "Description": "Created by Queue" + })); - const ref = queue.export(); + test.done(); + }, - QueueRef.import(stack, 'Imported', ref); + 'fails if trying to subscribe to a queue with managed kms encryption'(test: Test) { + const stack = new Stack(); + const queue = new sqs.Queue(stack, 'Queue', { encryption: sqs.QueueEncryption.KmsManaged }); + const bucket = new s3.Bucket(stack, 'Bucket'); + test.throws(() => bucket.onObjectRemoved(queue), 'Unable to add statement to IAM resource policy for KMS key: "alias/aws/sqs"'); + test.done(); + } - test.done(); } }; From d53d6b1ea14439c208c6d80e6eb1ba7a87a5d8a2 Mon Sep 17 00:00:00 2001 From: Doug-AWS Date: Tue, 14 Aug 2018 12:32:43 -0700 Subject: [PATCH 17/33] Fix to doc topic move --- packages/aws-cdk-docs/src/howto.rst | 124 ++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 packages/aws-cdk-docs/src/howto.rst diff --git a/packages/aws-cdk-docs/src/howto.rst b/packages/aws-cdk-docs/src/howto.rst new file mode 100644 index 0000000000000..21bf4c79d137b --- /dev/null +++ b/packages/aws-cdk-docs/src/howto.rst @@ -0,0 +1,124 @@ +.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 + International License (the "License"). You may not use this file except in compliance with the + License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. + + This file 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. + +.. note:: These instructions are only for the Amazon-internal preview of the |cdk|. + +.. _how_to: + +###################################### +Performing Common Tasks With the |cdk| +###################################### + +This topic describes how to perform some common tasks with the |cdk|. +Each section starts with the question "How do I" and supplies an answer. +Note that there are often multiple ways of performing a task. +This topic describes the simplest, most straightforward way. + +.. _how_to_use_cfn_template: + +How do I use an Existing |CFN| Template? +======================================== + +The |cdk| provides a mechanism that you can use in your code to include an +existing |CFN| template in your app. + +The following example includes the template *my-template.json* into the +existing app and gets the ARN of the bucket **mybucket** from the +template. + +.. code-block:: js + + import { FnGetAtt } from '@aws-cdk/core' + import { readFileSync } from 'fs' + + new Include(this, 'ExistingInfra', { + template: JSON.parse(readFileSync('my-template.json').toString)) + }); + + // To get the bucket's ARN: + const bucketArn = new FnGetAtt('mybucket', 'Arn'); + +The *my-template.json' must have the following resource, +where **abcdwxyz** is the unique, 8-character hash that the |cdk| generates for the resource: + +.. code-block:: json + + "TheBucketabcdwxyz": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketName": "mybucket" + } + } + +.. _how_to_add_a_stack: + +How do I Add a Stack to My App? +=============================== + +To add a stack to your app, +create a new class that extends the +::py:class:`_aws-cdk_core.Stack` class, +as shown in the following example. + +.. code-block:: js + + import { Stack, StackProps } from '@aws-cdk/core'; + // ... + class HelloStack extends Stack { + constructor(parent: App, name: string, props?: StackProps) { + super(parent, name, props); + } + } + +.. _how_to_create_stack_in_region: + +How do I Create a Stack in a Specific Region? +============================================= + +To create a stack in **us-west-2** in your app, +and set the **region** property of the **env** +member of the **StackProps** argument to your stack when creating it, +as shown in the following example. + +.. code-block:: js + + new HelloStack(this, 'hello-cdk-us-west-2', { + env: { + region: 'us-west-2' + }}); + +.. _how_to_azs: + +How do I Get the Availability Zones in a Region? +================================================ + +You can get the AZs in the current region by using the +::py:class:`_aws-cdk_core.AvailabilityZoneProvider` class, +as shown in the following example. + +.. code-block:: js + + const zones: string[] = new AvailabilityZoneProvider(this).availabilityZones; + + for (let zone of zones) { + // do somethning for each zone! + } + +.. _how_to_: + +How do I ? +======================================== + + +.. _how_to_: + +How do I ? +======================================== + From 389e9bc53a965958e9e2dc8e8e8b579c0b29615d Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Tue, 14 Aug 2018 13:01:57 -0700 Subject: [PATCH 18/33] refactor(aws-codepipeline): Re-structure the CodePipeline API. (#459) BREAKING CHANGE: 1. Introduce a new aws-codepipeline-api package, and move the Actions API there. 2. Move the service-specific Actions back into their service packages. --- .../.npmignore | 11 - .../aws-cloudformation-codepipeline/README.md | 24 - .../lib/index.ts | 1 - .../package.json | 67 --- .../tslint.json | 38 -- .../@aws-cdk/aws-cloudformation/README.md | 26 + .../@aws-cdk/aws-cloudformation/lib/index.ts | 4 +- .../lib/pipeline-actions.ts | 37 +- .../@aws-cdk/aws-cloudformation/package.json | 6 + .../aws-codebuild-codepipeline/.gitignore | 17 - .../aws-codebuild-codepipeline/.npmignore | 19 - .../aws-codebuild-codepipeline/LICENSE | 201 ------- .../aws-codebuild-codepipeline/NOTICE | 2 - .../aws-codebuild-codepipeline/README.md | 35 -- .../aws-codebuild-codepipeline/lib/index.ts | 2 - .../lib/pipeline-project.ts | 19 - .../aws-codebuild-codepipeline/package.json | 62 -- packages/@aws-cdk/aws-codebuild/README.md | 58 +- packages/@aws-cdk/aws-codebuild/lib/index.ts | 2 + .../lib/pipeline-actions.ts | 17 +- .../aws-codebuild/lib/pipeline-project.ts | 21 + packages/@aws-cdk/aws-codebuild/package.json | 1 + .../aws-codecommit-codepipeline/.gitignore | 17 - .../aws-codecommit-codepipeline/.npmignore | 19 - .../aws-codecommit-codepipeline/LICENSE | 201 ------- .../aws-codecommit-codepipeline/NOTICE | 2 - .../aws-codecommit-codepipeline/README.md | 21 - .../aws-codecommit-codepipeline/lib/index.ts | 1 - .../test/test.pipeline-action.ts | 45 -- packages/@aws-cdk/aws-codecommit/README.md | 34 +- packages/@aws-cdk/aws-codecommit/lib/index.ts | 1 + .../lib/pipeline-action.ts | 22 +- packages/@aws-cdk/aws-codecommit/package.json | 1 + .../.gitignore | 1 + .../.npmignore | 0 .../LICENSE | 0 .../NOTICE | 0 .../@aws-cdk/aws-codepipeline-api/README.md | 10 + .../aws-codepipeline-api/lib/action.ts | 313 ++++++++++ .../lib/artifact.ts | 2 +- .../aws-codepipeline-api/lib/build-action.ts | 55 ++ .../aws-codepipeline-api/lib/deploy-action.ts | 22 + .../aws-codepipeline-api/lib/index.ts | 6 + .../aws-codepipeline-api/lib/source-action.ts | 66 ++ .../lib/validation.ts | 4 +- .../package.json | 28 +- packages/@aws-cdk/aws-codepipeline/README.md | 7 +- .../@aws-cdk/aws-codepipeline/lib/actions.ts | 562 ------------------ .../lib/github-source-action.ts | 70 +++ .../@aws-cdk/aws-codepipeline/lib/index.ts | 5 +- .../lib/manual-approval-action.ts | 20 + .../@aws-cdk/aws-codepipeline/lib/pipeline.ts | 6 +- .../aws-codepipeline/lib/s3-source-action.ts | 53 ++ .../@aws-cdk/aws-codepipeline/lib/stage.ts | 63 +- .../@aws-cdk/aws-codepipeline/package.json | 11 +- ....cfn-template-from-repo.lit.expected.json} | 0 .../test/integ.cfn-template-from-repo.lit.ts} | 20 +- .../test/integ.lambda-pipeline.expected.json} | 0 .../test/integ.lambda-pipeline.ts} | 9 +- .../test/integ.pipeline-cfn.expected.json} | 0 .../test/integ.pipeline-cfn.ts} | 21 +- ...g.pipeline-code-commit-build.expected.json | 0 .../test/integ.pipeline-code-commit-build.ts | 20 +- .../integ.pipeline-code-commit.expected.json | 0 .../test/integ.pipeline-code-commit.ts | 10 +- .../test/integ.pipeline-events.expected.json | 0 .../test/integ.pipeline-events.ts | 23 +- .../aws-codepipeline/test/test.action.ts | 60 +- .../test.cloudformation-pipeline-actions.ts} | 33 +- .../test/test.general-validation.ts | 12 +- .../test/test.pipeline.ts | 159 ++++- .../aws-lambda-codepipeline/.gitignore | 16 - .../@aws-cdk/aws-lambda-codepipeline/LICENSE | 201 ------- .../@aws-cdk/aws-lambda-codepipeline/NOTICE | 2 - .../aws-lambda-codepipeline/README.md | 24 - .../aws-lambda-codepipeline/lib/index.ts | 1 - .../aws-lambda-codepipeline/package.json | 63 -- .../test/test.pipeline-action.ts | 91 --- packages/@aws-cdk/aws-lambda/README.md | 23 + packages/@aws-cdk/aws-lambda/lib/index.ts | 1 + .../lib/pipeline-action.ts | 15 +- packages/@aws-cdk/aws-lambda/package.json | 4 + 82 files changed, 1175 insertions(+), 1971 deletions(-) delete mode 100644 packages/@aws-cdk/aws-cloudformation-codepipeline/.npmignore delete mode 100644 packages/@aws-cdk/aws-cloudformation-codepipeline/README.md delete mode 100644 packages/@aws-cdk/aws-cloudformation-codepipeline/lib/index.ts delete mode 100644 packages/@aws-cdk/aws-cloudformation-codepipeline/package.json delete mode 100644 packages/@aws-cdk/aws-cloudformation-codepipeline/tslint.json rename packages/@aws-cdk/{aws-cloudformation-codepipeline => aws-cloudformation}/lib/pipeline-actions.ts (89%) delete mode 100644 packages/@aws-cdk/aws-codebuild-codepipeline/.gitignore delete mode 100644 packages/@aws-cdk/aws-codebuild-codepipeline/.npmignore delete mode 100644 packages/@aws-cdk/aws-codebuild-codepipeline/LICENSE delete mode 100644 packages/@aws-cdk/aws-codebuild-codepipeline/NOTICE delete mode 100644 packages/@aws-cdk/aws-codebuild-codepipeline/README.md delete mode 100644 packages/@aws-cdk/aws-codebuild-codepipeline/lib/index.ts delete mode 100644 packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-project.ts delete mode 100644 packages/@aws-cdk/aws-codebuild-codepipeline/package.json rename packages/@aws-cdk/{aws-codebuild-codepipeline => aws-codebuild}/lib/pipeline-actions.ts (73%) create mode 100644 packages/@aws-cdk/aws-codebuild/lib/pipeline-project.ts delete mode 100644 packages/@aws-cdk/aws-codecommit-codepipeline/.gitignore delete mode 100644 packages/@aws-cdk/aws-codecommit-codepipeline/.npmignore delete mode 100644 packages/@aws-cdk/aws-codecommit-codepipeline/LICENSE delete mode 100644 packages/@aws-cdk/aws-codecommit-codepipeline/NOTICE delete mode 100644 packages/@aws-cdk/aws-codecommit-codepipeline/README.md delete mode 100644 packages/@aws-cdk/aws-codecommit-codepipeline/lib/index.ts delete mode 100644 packages/@aws-cdk/aws-codecommit-codepipeline/test/test.pipeline-action.ts rename packages/@aws-cdk/{aws-codecommit-codepipeline => aws-codecommit}/lib/pipeline-action.ts (70%) rename packages/@aws-cdk/{aws-cloudformation-codepipeline => aws-codepipeline-api}/.gitignore (96%) rename packages/@aws-cdk/{aws-lambda-codepipeline => aws-codepipeline-api}/.npmignore (100%) rename packages/@aws-cdk/{aws-cloudformation-codepipeline => aws-codepipeline-api}/LICENSE (100%) rename packages/@aws-cdk/{aws-cloudformation-codepipeline => aws-codepipeline-api}/NOTICE (100%) create mode 100644 packages/@aws-cdk/aws-codepipeline-api/README.md create mode 100644 packages/@aws-cdk/aws-codepipeline-api/lib/action.ts rename packages/@aws-cdk/{aws-codepipeline => aws-codepipeline-api}/lib/artifact.ts (98%) create mode 100644 packages/@aws-cdk/aws-codepipeline-api/lib/build-action.ts create mode 100644 packages/@aws-cdk/aws-codepipeline-api/lib/deploy-action.ts create mode 100644 packages/@aws-cdk/aws-codepipeline-api/lib/index.ts create mode 100644 packages/@aws-cdk/aws-codepipeline-api/lib/source-action.ts rename packages/@aws-cdk/{aws-codepipeline => aws-codepipeline-api}/lib/validation.ts (97%) rename packages/@aws-cdk/{aws-codecommit-codepipeline => aws-codepipeline-api}/package.json (68%) delete mode 100644 packages/@aws-cdk/aws-codepipeline/lib/actions.ts create mode 100644 packages/@aws-cdk/aws-codepipeline/lib/github-source-action.ts create mode 100644 packages/@aws-cdk/aws-codepipeline/lib/manual-approval-action.ts create mode 100644 packages/@aws-cdk/aws-codepipeline/lib/s3-source-action.ts rename packages/@aws-cdk/{aws-cloudformation-codepipeline/test/integ.template-from-repo.lit.expected.json => aws-codepipeline/test/integ.cfn-template-from-repo.lit.expected.json} (100%) rename packages/@aws-cdk/{aws-cloudformation-codepipeline/test/integ.template-from-repo.lit.ts => aws-codepipeline/test/integ.cfn-template-from-repo.lit.ts} (66%) rename packages/@aws-cdk/{aws-lambda-codepipeline/test/integ.pipeline.expected.json => aws-codepipeline/test/integ.lambda-pipeline.expected.json} (100%) rename packages/@aws-cdk/{aws-lambda-codepipeline/test/integ.pipeline.ts => aws-codepipeline/test/integ.lambda-pipeline.ts} (79%) rename packages/@aws-cdk/{aws-cloudformation-codepipeline/test/integ.pipeline.expected.json => aws-codepipeline/test/integ.pipeline-cfn.expected.json} (100%) rename packages/@aws-cdk/{aws-cloudformation-codepipeline/test/integ.pipeline.ts => aws-codepipeline/test/integ.pipeline-cfn.ts} (67%) rename packages/@aws-cdk/{aws-codebuild-codepipeline => aws-codepipeline}/test/integ.pipeline-code-commit-build.expected.json (100%) rename packages/@aws-cdk/{aws-codebuild-codepipeline => aws-codepipeline}/test/integ.pipeline-code-commit-build.ts (58%) rename packages/@aws-cdk/{aws-codecommit-codepipeline => aws-codepipeline}/test/integ.pipeline-code-commit.expected.json (100%) rename packages/@aws-cdk/{aws-codecommit-codepipeline => aws-codepipeline}/test/integ.pipeline-code-commit.ts (72%) rename packages/@aws-cdk/{aws-codebuild-codepipeline => aws-codepipeline}/test/integ.pipeline-events.expected.json (100%) rename packages/@aws-cdk/{aws-codebuild-codepipeline => aws-codepipeline}/test/integ.pipeline-events.ts (66%) rename packages/@aws-cdk/{aws-cloudformation-codepipeline/test/test.pipeline-actions.ts => aws-codepipeline/test/test.cloudformation-pipeline-actions.ts} (90%) rename packages/@aws-cdk/{aws-codebuild-codepipeline => aws-codepipeline}/test/test.pipeline.ts (56%) delete mode 100644 packages/@aws-cdk/aws-lambda-codepipeline/.gitignore delete mode 100644 packages/@aws-cdk/aws-lambda-codepipeline/LICENSE delete mode 100644 packages/@aws-cdk/aws-lambda-codepipeline/NOTICE delete mode 100644 packages/@aws-cdk/aws-lambda-codepipeline/README.md delete mode 100644 packages/@aws-cdk/aws-lambda-codepipeline/lib/index.ts delete mode 100644 packages/@aws-cdk/aws-lambda-codepipeline/package.json delete mode 100644 packages/@aws-cdk/aws-lambda-codepipeline/test/test.pipeline-action.ts rename packages/@aws-cdk/{aws-lambda-codepipeline => aws-lambda}/lib/pipeline-action.ts (85%) diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/.npmignore b/packages/@aws-cdk/aws-cloudformation-codepipeline/.npmignore deleted file mode 100644 index f536f042b8cb6..0000000000000 --- a/packages/@aws-cdk/aws-cloudformation-codepipeline/.npmignore +++ /dev/null @@ -1,11 +0,0 @@ -# Don't include original .ts files when doing `npm pack` -*.ts -!*.d.ts -coverage -.nyc_output -*.tgz - -dist -.LAST_PACKAGE -.LAST_BUILD -!*.js \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/README.md b/packages/@aws-cdk/aws-cloudformation-codepipeline/README.md deleted file mode 100644 index 4960c60e24674..0000000000000 --- a/packages/@aws-cdk/aws-cloudformation-codepipeline/README.md +++ /dev/null @@ -1,24 +0,0 @@ -## AWS CodePipline Actions for AWS CloudFormation - -This module contains Actions that allows you to deploy to AWS CloudFormation from AWS CodePipeline. - -For example, the following code fragment defines a pipeline that automatically deploys a CloudFormation template -directly from a CodeCommit repository, with a manual approval step in between to confirm the changes: - -[example Pipeline to deploy CloudFormation](test/integ.template-from-repo.lit.ts) - -See [the AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline.html) -for more details about using CloudFormation in CodePipeline. - -### Actions defined by this package - -This package defines the following actions: - -* **CreateUpdateStack** - Deploy a CloudFormation template directly from the pipeline. The indicated stack is created, - or updated if it already exists. If the stack is in a failure state, deployment will fail (unless `replaceOnFailure` - is set to `true`, in which case it will be destroyed and recreated). -* **DeleteStackOnly** - Delete the stack with the given name. -* **CreateReplaceChangeSet** - Prepare a change set to be applied later. You will typically use change sets if you want - to manually verify the changes that are being staged, or if you want to separate the people (or system) preparing the - changes from the people (or system) applying the changes. -* **ExecuteChangeSet** - Execute a change set prepared previously. diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/lib/index.ts b/packages/@aws-cdk/aws-cloudformation-codepipeline/lib/index.ts deleted file mode 100644 index 9b3db900186ee..0000000000000 --- a/packages/@aws-cdk/aws-cloudformation-codepipeline/lib/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './pipeline-actions'; diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/package.json b/packages/@aws-cdk/aws-cloudformation-codepipeline/package.json deleted file mode 100644 index e3743f41783ee..0000000000000 --- a/packages/@aws-cdk/aws-cloudformation-codepipeline/package.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "name": "@aws-cdk/aws-cloudformation-codepipeline", - "version": "0.8.1", - "description": "AWS CodePipeline Actions for AWS CloudFormation", - "main": "lib/index.js", - "types": "lib/index.d.ts", - "jsii": { - "outdir": "dist", - "targets": { - "sphinx": {}, - "java": { - "package": "software.amazon.awscdk.services.cloudformation.codepipeline", - "maven": { - "groupId": "software.amazon.awscdk", - "artifactId": "cloudformation-codepipeline" - } - } - } - }, - "repository": { - "type": "git", - "url": "https://github.com/awslabs/aws-cdk.git" - }, - "scripts": { - "build": "cdk-build", - "watch": "cdk-watch", - "lint": "cdk-lint", - "test": "cdk-test", - "integ": "cdk-integ", - "pkglint": "pkglint -f", - "package": "cdk-package" - }, - "nyc": { - "lines": 60, - "branches": 30 - }, - "keywords": [ - "aws", - "cdk", - "codepipeline", - "constructs", - "cloudformation" - ], - "author": { - "name": "Amazon Web Services", - "url": "https://aws.amazon.com", - "organization": true - }, - "license": "Apache-2.0", - "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "@aws-cdk/aws-codebuild": "^0.8.1", - "@aws-cdk/aws-codebuild-codepipeline": "^0.8.1", - "@aws-cdk/aws-codecommit": "^0.8.1", - "@aws-cdk/aws-codecommit-codepipeline": "^0.8.1", - "@aws-cdk/aws-s3": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "pkglint": "^0.8.1" - }, - "dependencies": { - "@aws-cdk/aws-codepipeline": "^0.8.1", - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" - }, - "homepage": "https://github.com/awslabs/aws-cdk" -} diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/tslint.json b/packages/@aws-cdk/aws-cloudformation-codepipeline/tslint.json deleted file mode 100644 index ddd9bc8e0f437..0000000000000 --- a/packages/@aws-cdk/aws-cloudformation-codepipeline/tslint.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "extends": "tslint:recommended", - "rules": { - "semicolon": [ - true, - "always", - "ignore-interfaces" - ], - "no-invalid-template-strings": false, - "quotemark": false, - "interface-name": false, - "max-classes-per-file": false, - "member-access": { - "severity": "warning" - }, - "interface-over-type-literal": false, - "eofline": false, - "arrow-parens": false, - "no-namespace": false, - "max-line-length": [ - true, - 150 - ], - "object-literal-sort-keys": false, - "trailing-comma": false, - "no-unused-expression": [ - true, - "allow-new" - ], - "variable-name": [ - true, - "ban-keywords", - "check-format", - "allow-leading-underscore", - "allow-pascal-case" - ] - } -} diff --git a/packages/@aws-cdk/aws-cloudformation/README.md b/packages/@aws-cdk/aws-cloudformation/README.md index c7b2799faa549..044f9ecab0780 100644 --- a/packages/@aws-cdk/aws-cloudformation/README.md +++ b/packages/@aws-cdk/aws-cloudformation/README.md @@ -1,6 +1,32 @@ ## CDK Constructs for AWS CloudFormation + This module is part of the [AWS Cloud Development Kit](https://github.com/awslabs/aws-cdk) project. +### CodePipeline Actions for CloudFormation + +This module contains Actions that allows you to deploy to CloudFormation from AWS CodePipeline. + +For example, the following code fragment defines a pipeline that automatically deploys a CloudFormation template +directly from a CodeCommit repository, with a manual approval step in between to confirm the changes: + +[example Pipeline to deploy CloudFormation](../aws-codepipeline/test/integ.cfn-template-from-repo.lit.ts) + +See [the AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline.html) +for more details about using CloudFormation in CodePipeline. + +#### Actions defined by this package + +This package defines the following actions: + +* **CreateUpdateStack** - Deploy a CloudFormation template directly from the pipeline. The indicated stack is created, + or updated if it already exists. If the stack is in a failure state, deployment will fail (unless `replaceOnFailure` + is set to `true`, in which case it will be destroyed and recreated). +* **DeleteStackOnly** - Delete the stack with the given name. +* **CreateReplaceChangeSet** - Prepare a change set to be applied later. You will typically use change sets if you want + to manually verify the changes that are being staged, or if you want to separate the people (or system) preparing the + changes from the people (or system) applying the changes. +* **ExecuteChangeSet** - Execute a change set prepared previously. + ### Custom Resources Custom Resources are CloudFormation resources that are implemented by diff --git a/packages/@aws-cdk/aws-cloudformation/lib/index.ts b/packages/@aws-cdk/aws-cloudformation/lib/index.ts index 51f1795578d4d..2a9dc1178f2fe 100644 --- a/packages/@aws-cdk/aws-cloudformation/lib/index.ts +++ b/packages/@aws-cdk/aws-cloudformation/lib/index.ts @@ -1,3 +1,5 @@ +export * from './custom-resource'; +export * from './pipeline-actions'; + // AWS::CloudFormation CloudFormation Resources: export * from './cloudformation.generated'; -export * from './custom-resource'; diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/lib/pipeline-actions.ts b/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts similarity index 89% rename from packages/@aws-cdk/aws-cloudformation-codepipeline/lib/pipeline-actions.ts rename to packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts index 69209fe58bae2..0538704f2e422 100644 --- a/packages/@aws-cdk/aws-cloudformation-codepipeline/lib/pipeline-actions.ts +++ b/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts @@ -1,11 +1,11 @@ -import codepipeline = require('@aws-cdk/aws-codepipeline'); +import codepipeline = require('@aws-cdk/aws-codepipeline-api'); import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); /** * Properties common to all CloudFormation actions */ -export interface CloudFormationCommonProps { +export interface CloudFormationCommonProps extends codepipeline.CommonActionProps { /** * The name of the stack to apply this action to */ @@ -45,15 +45,26 @@ export abstract class CloudFormationAction extends codepipeline.DeployAction { */ public artifact?: codepipeline.Artifact; - constructor(parent: codepipeline.Stage, id: string, props: CloudFormationCommonProps, configuration?: any) { - super(parent, id, 'CloudFormation', { minInputs: 0, maxInputs: 10, minOutputs: 0, maxOutputs: 1 }, { - ...configuration, - StackName: props.stackName, - OutputFileName: props.outputFileName, + constructor(parent: cdk.Construct, id: string, props: CloudFormationCommonProps, configuration?: any) { + super(parent, id, { + stage: props.stage, + artifactBounds: { + minInputs: 0, + maxInputs: 10, + minOutputs: 0, + maxOutputs: 1, + }, + provider: 'CloudFormation', + configuration: { + StackName: props.stackName, + OutputFileName: props.outputFileName, + ...configuration, + } }); if (props.outputFileName) { - this.artifact = this.addOutputArtifact(props.outputArtifactName || (parent.id + this.id + 'Artifact')); + this.artifact = this.addOutputArtifact(props.outputArtifactName || + (props.stage.name + this.id + 'Artifact')); } } } @@ -72,7 +83,7 @@ export interface ExecuteChangeSetProps extends CloudFormationCommonProps { * CodePipeline action to execute a prepared change set. */ export class ExecuteChangeSet extends CloudFormationAction { - constructor(parent: codepipeline.Stage, id: string, props: ExecuteChangeSetProps) { + constructor(parent: cdk.Construct, id: string, props: ExecuteChangeSetProps) { super(parent, id, props, { ActionMode: 'CHANGE_SET_EXECUTE', ChangeSetName: props.changeSetName, @@ -168,7 +179,7 @@ export interface CloudFormationDeploymentActionCommonProps extends CloudFormatio export abstract class CloudFormationDeploymentAction extends CloudFormationAction { public readonly role: iam.Role; - constructor(parent: codepipeline.Stage, id: string, props: CloudFormationDeploymentActionCommonProps, configuration: any) { + constructor(parent: cdk.Construct, id: string, props: CloudFormationDeploymentActionCommonProps, configuration: any) { const capabilities = props.fullPermissions && props.capabilities === undefined ? [CloudFormationCapabilities.NamedIAM] : props.capabilities; super(parent, id, props, { @@ -224,7 +235,7 @@ export interface CreateReplaceChangeSetProps extends CloudFormationDeploymentAct * If the change set exists, AWS CloudFormation deletes it, and then creates a new one. */ export class CreateReplaceChangeSet extends CloudFormationDeploymentAction { - constructor(parent: codepipeline.Stage, id: string, props: CreateReplaceChangeSetProps) { + constructor(parent: cdk.Construct, id: string, props: CreateReplaceChangeSetProps) { super(parent, id, props, { ActionMode: 'CHANGE_SET_REPLACE', ChangeSetName: props.changeSetName, @@ -275,7 +286,7 @@ export interface CreateUpdateProps extends CloudFormationDeploymentActionCommonP * troubleshooting them. You would typically choose this mode for testing. */ export class CreateUpdateStack extends CloudFormationDeploymentAction { - constructor(parent: codepipeline.Stage, id: string, props: CreateUpdateProps) { + constructor(parent: cdk.Construct, id: string, props: CreateUpdateProps) { super(parent, id, props, { ActionMode: props.replaceOnFailure ? 'REPLACE_ON_FAILURE' : 'CREATE_UPDATE', TemplatePath: props.templatePath.location @@ -298,7 +309,7 @@ export interface DeleteStackOnlyProps extends CloudFormationDeploymentActionComm * without deleting a stack. */ export class DeleteStackOnly extends CloudFormationDeploymentAction { - constructor(parent: codepipeline.Stage, id: string, props: DeleteStackOnlyProps) { + constructor(parent: cdk.Construct, id: string, props: DeleteStackOnlyProps) { super(parent, id, props, { ActionMode: 'DELETE_ONLY', }); diff --git a/packages/@aws-cdk/aws-cloudformation/package.json b/packages/@aws-cdk/aws-cloudformation/package.json index 8b11ef7286fe5..a3bbcb4e6c495 100644 --- a/packages/@aws-cdk/aws-cloudformation/package.json +++ b/packages/@aws-cdk/aws-cloudformation/package.json @@ -33,6 +33,10 @@ "cdk-build": { "cloudformation": "AWS::CloudFormation" }, + "nyc": { + "lines": 50, + "branches": 20 + }, "keywords": [ "aws", "cdk", @@ -54,6 +58,8 @@ }, "dependencies": { "@aws-cdk/cdk": "^0.8.1", + "@aws-cdk/aws-codepipeline-api": "^0.8.1", + "@aws-cdk/aws-iam": "^0.8.1", "@aws-cdk/aws-lambda": "^0.8.1", "@aws-cdk/aws-sns": "^0.8.1" }, diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/.gitignore b/packages/@aws-cdk/aws-codebuild-codepipeline/.gitignore deleted file mode 100644 index 2c2ba24ad6150..0000000000000 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -*.js -*.js.map -*.d.ts -node_modules -*.generated.ts -dist -tsconfig.json -tslint.json - -.jsii - -.LAST_BUILD -.nyc_output -coverage -.nycrc - -.LAST_PACKAGE \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/.npmignore b/packages/@aws-cdk/aws-codebuild-codepipeline/.npmignore deleted file mode 100644 index fd40d742b8c54..0000000000000 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/.npmignore +++ /dev/null @@ -1,19 +0,0 @@ -+# Don't include original .ts files when doing `npm pack` -+*.ts -+!*.d.ts -+coverage -+.nyc_output -+*.tgz - -dist -.LAST_PACKAGE -.LAST_BUILD -*.ts -!*.d.ts -!*.js -coverage -.nyc_output -*.tgz - -# Include .jsii -!.jsii diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/LICENSE b/packages/@aws-cdk/aws-codebuild-codepipeline/LICENSE deleted file mode 100644 index 1739faaebb745..0000000000000 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/NOTICE b/packages/@aws-cdk/aws-codebuild-codepipeline/NOTICE deleted file mode 100644 index 95fd48569c743..0000000000000 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/NOTICE +++ /dev/null @@ -1,2 +0,0 @@ -AWS Cloud Development Kit (AWS CDK) -Copyright 2018-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/README.md b/packages/@aws-cdk/aws-codebuild-codepipeline/README.md deleted file mode 100644 index dbbd8e1d01a2f..0000000000000 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/README.md +++ /dev/null @@ -1,35 +0,0 @@ -## AWS CodePipline Actions for AWS CodeBuild - -This module contains an Action that allows you to use a CodeBuild Project in CodePipeline. - -Example: - -```ts -import codebuildPipeline = require('@aws-cdk/aws-codebuild-codepipeline'); -import codepipeline = require('@aws-cdk/aws-codepipeline'); - -// see the @aws-cdk/aws-codebuild module for more documentation on how to create CodeBuild Projects -const project = new codebuildPipeline.PipelineProject(this, 'MyProject', { - // ... -}); - -const pipeline = new codepipeline.Pipeline(this, 'MyPipeline'); -const buildStage = new codepipeline.Stage(pipeline, 'Build'); -new codebuildPipeline.PipelineBuildAction(buildStage, 'CodeBuild', { - project -}); -``` - -The `PipelineProject` utility class is a simple sugar around the `Project` -class from the `@aws-cdk/aws-codebuild` module, -it's equivalent to: - -```ts -import codebuild = require('@aws-cdk/aws-codebuild'); - -const project = new codebuild.Project(this, 'MyProject', { - source: new codebuild.CodePipelineSource(), - artifacts: new codebuild.CodePipelineBuildArtifacts(), - // rest of the properties from PipelineProject are passed unchanged... -} -``` diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/lib/index.ts b/packages/@aws-cdk/aws-codebuild-codepipeline/lib/index.ts deleted file mode 100644 index 7dda267e2867d..0000000000000 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/lib/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './pipeline-actions'; -export * from './pipeline-project'; diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-project.ts b/packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-project.ts deleted file mode 100644 index 452757e84ab7d..0000000000000 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-project.ts +++ /dev/null @@ -1,19 +0,0 @@ -import codebuild = require('@aws-cdk/aws-codebuild'); -import cdk = require('@aws-cdk/cdk'); - -// tslint:disable-next-line:no-empty-interface -export interface PipelineProjectProps extends codebuild.CommonProjectProps { -} - -/** - * A convenience class for CodeBuild Projects that are used in CodePipeline. - */ -export class PipelineProject extends codebuild.Project { - constructor(parent: cdk.Construct, id: string, props?: PipelineProjectProps) { - super(parent, id, { - source: new codebuild.CodePipelineSource(), - artifacts: new codebuild.CodePipelineBuildArtifacts(), - ...props - }); - } -} diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/package.json b/packages/@aws-cdk/aws-codebuild-codepipeline/package.json deleted file mode 100644 index 95dd64cfd63b7..0000000000000 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/package.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "name": "@aws-cdk/aws-codebuild-codepipeline", - "version": "0.8.1", - "description": "AWS CodePipline Actions for AWS CodeBuild", - "main": "lib/index.js", - "types": "lib/index.d.ts", - "jsii": { - "outdir": "dist", - "targets": { - "java": { - "package": "software.amazon.awscdk.services.codebuild.codepipeline", - "maven": { - "groupId": "software.amazon.awscdk", - "artifactId": "codebuild-codepipeline" - } - }, - "sphinx": {} - } - }, - "repository": { - "type": "git", - "url": "https://github.com/awslabs/aws-cdk.git" - }, - "scripts": { - "build": "cdk-build", - "watch": "cdk-watch", - "lint": "cdk-lint", - "test": "cdk-test", - "integ": "cdk-integ", - "pkglint": "pkglint -f", - "package": "cdk-package" - }, - "keywords": [ - "aws", - "cdk", - "codebuild", - "codepipeline", - "constructs" - ], - "author": { - "name": "Amazon Web Services", - "url": "https://aws.amazon.com", - "organization": true - }, - "license": "Apache-2.0", - "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "pkglint": "^0.8.1" - }, - "dependencies": { - "@aws-cdk/aws-codebuild": "^0.8.1", - "@aws-cdk/aws-codecommit": "^0.8.1", - "@aws-cdk/aws-codecommit-codepipeline": "^0.8.1", - "@aws-cdk/aws-codepipeline": "^0.8.1", - "@aws-cdk/aws-s3": "^0.8.1", - "@aws-cdk/aws-sns": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" - }, - "homepage": "https://github.com/awslabs/aws-cdk" -} diff --git a/packages/@aws-cdk/aws-codebuild/README.md b/packages/@aws-cdk/aws-codebuild/README.md index 9b48621af3d7a..7e96f68ca03ec 100644 --- a/packages/@aws-cdk/aws-codebuild/README.md +++ b/packages/@aws-cdk/aws-codebuild/README.md @@ -1,16 +1,10 @@ ## AWS CodeBuild Construct Library -Define a project. This will also create an IAM Role and IAM Policy for CodeBuildRole to use. +Define a project. This will also create an IAM Role and IAM Policy for CodeBuild to use. -Create a CodeBuild project with CodePipeline as the source: +### Using CodeBuild with other AWS services -```ts -import codebuild = require('@aws-cdk/aws-codebuild'); - -new codebuild.Project(this, 'MyFirstProject', { - source: new codebuild.CodePipelineSource() -}); -``` +#### CodeCommit Create a CodeBuild project with CodeCommit as the source: @@ -24,6 +18,8 @@ new codebuild.Project(this, 'MyFirstCodeCommitProject', { }); ``` +#### S3 + Create a CodeBuild project with an S3 bucket as the source: ```ts @@ -36,6 +32,50 @@ new codebuild.Project(this, 'MyProject', { }); ``` +#### CodePipeline + +Example of a Project used in CodePipeline, +alongside CodeCommit: + +```ts +import codebuild = require('@aws-cdk/aws-codebuild'); +import codecommit = require('@aws-cdk/aws-codecommit'); +import codepipeline = require('@aws-cdk/aws-codepipeline'); + +const repository = new codecommit.Repository(this, 'MyRepository', { + repositoryName: 'MyRepository', +}); + +const project = new codebuild.PipelineProject(this, 'MyProject'); + +const pipeline = new codepipeline.Pipeline(this, 'MyPipeline'); + +const sourceStage = new codepipeline.Stage(pipeline, 'Source'); +const sourceAction = new codecommit.PipelineSource(this, 'CodeCommit', { + stage: sourceStage, + artifactName: 'SourceOutput', + repository, +}); + +const buildStage = new codepipeline.Stage(pipeline, 'Build'); +new codebuild.PipelineBuildAction(this, 'CodeBuild', { + stage: buildStage, + inputArtifact: sourceAction.artifact, + project, +}); +``` + +The `PipelineProject` utility class is a simple sugar around the `Project` class, +it's equivalent to: + +```ts +const project = new codebuild.Project(this, 'MyProject', { + source: new codebuild.CodePipelineSource(), + artifacts: new codebuild.CodePipelineBuildArtifacts(), + // rest of the properties from PipelineProject are passed unchanged... +} +``` + ### Using Project as an event target The `Project` construct implements the `IEventRuleTarget` interface. This means that it can be diff --git a/packages/@aws-cdk/aws-codebuild/lib/index.ts b/packages/@aws-cdk/aws-codebuild/lib/index.ts index 55df0a8ad66cd..77571a2a92c05 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/index.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/index.ts @@ -1,3 +1,5 @@ +export * from './pipeline-actions'; +export * from './pipeline-project'; export * from './project'; export * from './source'; export * from './artifacts'; diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-actions.ts b/packages/@aws-cdk/aws-codebuild/lib/pipeline-actions.ts similarity index 73% rename from packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-actions.ts rename to packages/@aws-cdk/aws-codebuild/lib/pipeline-actions.ts index 66be0d97b6575..62ffb9198e4da 100644 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/lib/pipeline-actions.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/pipeline-actions.ts @@ -1,11 +1,11 @@ -import codebuild = require('@aws-cdk/aws-codebuild'); -import codepipeline = require('@aws-cdk/aws-codepipeline'); +import codepipeline = require('@aws-cdk/aws-codepipeline-api'); import cdk = require('@aws-cdk/cdk'); +import { ProjectRef } from './project'; /** * Construction properties of the {@link PipelineBuildAction CodeBuild build CodePipeline Action}. */ -export interface PipelineBuildActionProps { +export interface PipelineBuildActionProps extends codepipeline.CommonActionProps { /** * The source to use as input for this build */ @@ -19,18 +19,19 @@ export interface PipelineBuildActionProps { /** * The build project */ - project: codebuild.ProjectRef; + project: ProjectRef; } /** * CodePipeline build Action that uses AWS CodeBuild. */ export class PipelineBuildAction extends codepipeline.BuildAction { - constructor(parent: codepipeline.Stage, name: string, props: PipelineBuildActionProps) { + constructor(parent: cdk.Construct, name: string, props: PipelineBuildActionProps) { // This happened when ProjectName was accidentally set to the project's ARN: // https://qiita.com/ikeisuke/items/2fbc0b80b9bbd981b41f super(parent, name, { + stage: props.stage, provider: 'CodeBuild', inputArtifact: props.inputArtifact, artifactName: props.artifactName, @@ -45,12 +46,14 @@ export class PipelineBuildAction extends codepipeline.BuildAction { 'codebuild:StopBuild', ]; - parent.pipeline.addToRolePolicy(new cdk.PolicyStatement() + props.stage.pipelineRole.addToPolicy(new cdk.PolicyStatement() .addResource(props.project.projectArn) .addActions(...actions)); // allow codebuild to read and write artifacts to the pipline's artifact bucket. - parent.pipeline.artifactBucket.grantReadWrite(props.project.role); + if (props.project.role) { + props.stage.grantPipelineBucketReadWrite(props.project.role); + } // policy must be added as a dependency to the pipeline!! // TODO: grants - build.addResourcePermission() and also make sure permission diff --git a/packages/@aws-cdk/aws-codebuild/lib/pipeline-project.ts b/packages/@aws-cdk/aws-codebuild/lib/pipeline-project.ts new file mode 100644 index 0000000000000..af9ff405d6471 --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/lib/pipeline-project.ts @@ -0,0 +1,21 @@ +import cdk = require('@aws-cdk/cdk'); +import { CodePipelineBuildArtifacts } from './artifacts'; +import { CommonProjectProps, Project } from './project'; +import { CodePipelineSource } from './source'; + +// tslint:disable-next-line:no-empty-interface +export interface PipelineProjectProps extends CommonProjectProps { +} + +/** + * A convenience class for CodeBuild Projects that are used in CodePipeline. + */ +export class PipelineProject extends Project { + constructor(parent: cdk.Construct, id: string, props?: PipelineProjectProps) { + super(parent, id, { + source: new CodePipelineSource(), + artifacts: new CodePipelineBuildArtifacts(), + ...props + }); + } +} diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index bacffe769ee9c..c38854738f6aa 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -60,6 +60,7 @@ "dependencies": { "@aws-cdk/aws-cloudwatch": "^0.8.1", "@aws-cdk/aws-codecommit": "^0.8.1", + "@aws-cdk/aws-codepipeline-api": "^0.8.1", "@aws-cdk/aws-events": "^0.8.1", "@aws-cdk/aws-iam": "^0.8.1", "@aws-cdk/aws-kms": "^0.8.1", diff --git a/packages/@aws-cdk/aws-codecommit-codepipeline/.gitignore b/packages/@aws-cdk/aws-codecommit-codepipeline/.gitignore deleted file mode 100644 index 2c2ba24ad6150..0000000000000 --- a/packages/@aws-cdk/aws-codecommit-codepipeline/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -*.js -*.js.map -*.d.ts -node_modules -*.generated.ts -dist -tsconfig.json -tslint.json - -.jsii - -.LAST_BUILD -.nyc_output -coverage -.nycrc - -.LAST_PACKAGE \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codecommit-codepipeline/.npmignore b/packages/@aws-cdk/aws-codecommit-codepipeline/.npmignore deleted file mode 100644 index fd40d742b8c54..0000000000000 --- a/packages/@aws-cdk/aws-codecommit-codepipeline/.npmignore +++ /dev/null @@ -1,19 +0,0 @@ -+# Don't include original .ts files when doing `npm pack` -+*.ts -+!*.d.ts -+coverage -+.nyc_output -+*.tgz - -dist -.LAST_PACKAGE -.LAST_BUILD -*.ts -!*.d.ts -!*.js -coverage -.nyc_output -*.tgz - -# Include .jsii -!.jsii diff --git a/packages/@aws-cdk/aws-codecommit-codepipeline/LICENSE b/packages/@aws-cdk/aws-codecommit-codepipeline/LICENSE deleted file mode 100644 index 1739faaebb745..0000000000000 --- a/packages/@aws-cdk/aws-codecommit-codepipeline/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/packages/@aws-cdk/aws-codecommit-codepipeline/NOTICE b/packages/@aws-cdk/aws-codecommit-codepipeline/NOTICE deleted file mode 100644 index 95fd48569c743..0000000000000 --- a/packages/@aws-cdk/aws-codecommit-codepipeline/NOTICE +++ /dev/null @@ -1,2 +0,0 @@ -AWS Cloud Development Kit (AWS CDK) -Copyright 2018-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-codecommit-codepipeline/README.md b/packages/@aws-cdk/aws-codecommit-codepipeline/README.md deleted file mode 100644 index 15e3ce7e097f5..0000000000000 --- a/packages/@aws-cdk/aws-codecommit-codepipeline/README.md +++ /dev/null @@ -1,21 +0,0 @@ -## AWS CodePipline Actions for AWS CodeCommit - -This module contains an Action that allows you to use a CodeCommit Repository as a Source in CodePipeline. - -Example: - -```ts -import codecommit = require('@aws-cdk/aws-codecommit'); -import codecommitPipeline = require('@aws-cdk/aws-codecommit-codepipeline'); -import codepipeline = require('@aws-cdk/aws-codepipeline'); - -// see the @aws-cdk/aws-codecommit module for more documentation on how to create CodeCommit Repositories -const repository = new codecommit.Repository( // ... -); - -const pipeline = new codepipeline.Pipeline(this, 'MyPipeline'); -const sourceStage = new codepipeline.Stage(pipeline, 'Source'); -new codecommitPipeline.PipelineSource(sourceStage, 'CodeCommit', { - repository: repository, -}); -``` diff --git a/packages/@aws-cdk/aws-codecommit-codepipeline/lib/index.ts b/packages/@aws-cdk/aws-codecommit-codepipeline/lib/index.ts deleted file mode 100644 index 890162998e10d..0000000000000 --- a/packages/@aws-cdk/aws-codecommit-codepipeline/lib/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './pipeline-action'; diff --git a/packages/@aws-cdk/aws-codecommit-codepipeline/test/test.pipeline-action.ts b/packages/@aws-cdk/aws-codecommit-codepipeline/test/test.pipeline-action.ts deleted file mode 100644 index a71c24ffbb43d..0000000000000 --- a/packages/@aws-cdk/aws-codecommit-codepipeline/test/test.pipeline-action.ts +++ /dev/null @@ -1,45 +0,0 @@ -import codecommit = require("@aws-cdk/aws-codecommit"); -import codepipeline = require("@aws-cdk/aws-codepipeline"); -import cdk = require('@aws-cdk/cdk'); -import { Test } from 'nodeunit'; -import codecommitCodepipeline = require('../lib'); - -export = { - 'polling for changes': { - - 'does not poll for changes'(test: Test) { - const stack = new cdk.Stack(); - - const result = new codecommitCodepipeline.PipelineSource(stageForTesting(stack), 'stage', { - artifactName: 'SomeArtifact', - repository: repositoryForTesting(stack), - pollForSourceChanges: false - }); - test.equal(result.configuration.PollForSourceChanges, false); - test.done(); - }, - - 'polls for changes'(test: Test) { - const stack = new cdk.Stack(); - - const result = new codecommitCodepipeline.PipelineSource(stageForTesting(stack), 'stage', { - artifactName: 'SomeArtifact', - repository: repositoryForTesting(stack), - pollForSourceChanges: true - }); - test.equal(result.configuration.PollForSourceChanges, true); - test.done(); - } - } -}; - -function stageForTesting(stack: cdk.Stack): codepipeline.Stage { - const pipeline = new codepipeline.Pipeline(stack, 'pipeline'); - return new codepipeline.Stage(pipeline, 'stage'); -} - -function repositoryForTesting(stack: cdk.Stack): codecommit.Repository { - return new codecommit.Repository(stack, 'Repository', { - repositoryName: 'Repository' - }); -} diff --git a/packages/@aws-cdk/aws-codecommit/README.md b/packages/@aws-cdk/aws-codecommit/README.md index dfa40c3ab4a26..d268be37f5b9b 100644 --- a/packages/@aws-cdk/aws-codecommit/README.md +++ b/packages/@aws-cdk/aws-codecommit/README.md @@ -5,7 +5,7 @@ To add a CodeCommit Repository to your stack: ```ts import codecommit = require('@aws-cdk/aws-codecommit'); -const repository = new codecommit.Repository(stack, 'Repository' ,{ +const repository = new codecommit.Repository(this, 'Repository' ,{ repositoryName: 'MyRepositoryName' }); ``` @@ -15,7 +15,7 @@ To add an SNS trigger to your repository: ```ts import codecommit = require('@aws-cdk/aws-codecommit'); -const repository = new codecommit.Repository(stack, 'Repository', { +const repository = new codecommit.Repository(this, 'Repository', { repositoryName: 'MyRepositoryName' }); @@ -23,11 +23,35 @@ const repository = new codecommit.Repository(stack, 'Repository', { repository.notify('arn:aws:sns:*:123456789012:my_topic'); ``` +### CodePipeline + +To use a CodeCommit Repository in a CodePipeline: + +```ts +import codecommit = require('@aws-cdk/aws-codecommit'); +import codepipeline = require('@aws-cdk/aws-codepipeline'); + +// see above for the details... +const repository = new codecommit.Repository( // ... +); + +const pipeline = new codepipeline.Pipeline(this, 'MyPipeline', { + pipelineName: 'MyPipeline', +}); +const sourceStage = new codepipeline.Stage(pipeline, 'Source'); +const sourceAction = new codecommit.PipelineSource(this, 'CodeCommit', { + stage: sourceStage, + artifactName: 'SourceOutput', //name can be arbitrary + repository, +}); +// use sourceAction.artifact as the inputArtifact to later Actions... +``` + ### Events -CodeCommit repositories emit CloudWatch events for certain activity. Use the -`repo.onXxx` methods to define rules that trigger on these events and invoke -targets as a result: +CodeCommit repositories emit CloudWatch events for certain activity. +Use the `repo.onXxx` methods to define rules that trigger on these events +and invoke targets as a result: ```ts // starts a CodeBuild project when a commit is pushed to the "master" branch of the repo diff --git a/packages/@aws-cdk/aws-codecommit/lib/index.ts b/packages/@aws-cdk/aws-codecommit/lib/index.ts index 2fa63e2e6ef94..d2e8bb4a6494c 100644 --- a/packages/@aws-cdk/aws-codecommit/lib/index.ts +++ b/packages/@aws-cdk/aws-codecommit/lib/index.ts @@ -1,3 +1,4 @@ +export * from './pipeline-action'; export * from './repository'; // AWS::CodeCommit CloudFormation Resources: diff --git a/packages/@aws-cdk/aws-codecommit-codepipeline/lib/pipeline-action.ts b/packages/@aws-cdk/aws-codecommit/lib/pipeline-action.ts similarity index 70% rename from packages/@aws-cdk/aws-codecommit-codepipeline/lib/pipeline-action.ts rename to packages/@aws-cdk/aws-codecommit/lib/pipeline-action.ts index 7c6016378186c..76a118add732a 100644 --- a/packages/@aws-cdk/aws-codecommit-codepipeline/lib/pipeline-action.ts +++ b/packages/@aws-cdk/aws-codecommit/lib/pipeline-action.ts @@ -1,29 +1,30 @@ -import codecommit = require('@aws-cdk/aws-codecommit'); -import codepipeline = require('@aws-cdk/aws-codepipeline'); +import codepipeline = require('@aws-cdk/aws-codepipeline-api'); import cdk = require('@aws-cdk/cdk'); +import { RepositoryRef } from './repository'; /** * Construction properties of the {@link PipelineSource CodeCommit source CodePipeline Action}. */ -export interface PipelineSourceProps { +export interface PipelineSourceProps extends codepipeline.CommonActionProps { /** - * The name of the source's output artifact. Output artifacts are used by CodePipeline as - * inputs into other actions. + * The name of the source's output artifact. + * Output artifacts are used by CodePipeline as inputs into other actions. */ artifactName: string; /** * The CodeCommit repository. */ - repository: codecommit.RepositoryRef; + repository: RepositoryRef; /** * @default 'master' */ branch?: string; + // TODO: use CloudWatch events instead /** - * Whether or not AWS CodePipeline should poll for source changes + * Whether or not AWS CodePipeline should poll for source changes. * * @default true */ @@ -33,9 +34,10 @@ export interface PipelineSourceProps { /** * CodePipeline Source that is provided by an AWS CodeCommit repository. */ -export class PipelineSource extends codepipeline.Source { - constructor(parent: codepipeline.Stage, name: string, props: PipelineSourceProps) { +export class PipelineSource extends codepipeline.SourceAction { + constructor(parent: cdk.Construct, name: string, props: PipelineSourceProps) { super(parent, name, { + stage: props.stage, provider: 'CodeCommit', configuration: { RepositoryName: props.repository.repositoryName, @@ -54,7 +56,7 @@ export class PipelineSource extends codepipeline.Source { 'codecommit:CancelUploadArchive', ]; - parent.pipeline.addToRolePolicy(new cdk.PolicyStatement() + props.stage.pipelineRole.addToPolicy(new cdk.PolicyStatement() .addResource(props.repository.repositoryArn) .addActions(...actions)); } diff --git a/packages/@aws-cdk/aws-codecommit/package.json b/packages/@aws-cdk/aws-codecommit/package.json index f1617659121e0..42c7767b8f11e 100644 --- a/packages/@aws-cdk/aws-codecommit/package.json +++ b/packages/@aws-cdk/aws-codecommit/package.json @@ -59,6 +59,7 @@ "pkglint": "^0.8.1" }, "dependencies": { + "@aws-cdk/aws-codepipeline-api": "^0.8.1", "@aws-cdk/aws-events": "^0.8.1", "@aws-cdk/aws-iam": "^0.8.1", "@aws-cdk/cdk": "^0.8.1" diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/.gitignore b/packages/@aws-cdk/aws-codepipeline-api/.gitignore similarity index 96% rename from packages/@aws-cdk/aws-cloudformation-codepipeline/.gitignore rename to packages/@aws-cdk/aws-codepipeline-api/.gitignore index acfc6e27248fe..d25f002f4cf52 100644 --- a/packages/@aws-cdk/aws-cloudformation-codepipeline/.gitignore +++ b/packages/@aws-cdk/aws-codepipeline-api/.gitignore @@ -6,6 +6,7 @@ tslint.json *.generated.ts dist lib/generated/resources.ts +*.tgz .jsii .LAST_BUILD diff --git a/packages/@aws-cdk/aws-lambda-codepipeline/.npmignore b/packages/@aws-cdk/aws-codepipeline-api/.npmignore similarity index 100% rename from packages/@aws-cdk/aws-lambda-codepipeline/.npmignore rename to packages/@aws-cdk/aws-codepipeline-api/.npmignore diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/LICENSE b/packages/@aws-cdk/aws-codepipeline-api/LICENSE similarity index 100% rename from packages/@aws-cdk/aws-cloudformation-codepipeline/LICENSE rename to packages/@aws-cdk/aws-codepipeline-api/LICENSE diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/NOTICE b/packages/@aws-cdk/aws-codepipeline-api/NOTICE similarity index 100% rename from packages/@aws-cdk/aws-cloudformation-codepipeline/NOTICE rename to packages/@aws-cdk/aws-codepipeline-api/NOTICE diff --git a/packages/@aws-cdk/aws-codepipeline-api/README.md b/packages/@aws-cdk/aws-codepipeline-api/README.md new file mode 100644 index 0000000000000..2f19570b6d1e0 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-api/README.md @@ -0,0 +1,10 @@ +## AWS CodePipeline Actions API + +This package contains the abstract API of Pipeline Actions. +It's used by the `aws-codepipeline` module, +and the AWS service modules that integrate with AWS CodePipeline. + +You shoould never need to depend on it directly - +instead, depend on `aws-codepipeline`, +and the module you need the concrete Actions from +(like `aws-codecommit`, `aws-codebuild`, etc.). diff --git a/packages/@aws-cdk/aws-codepipeline-api/lib/action.ts b/packages/@aws-cdk/aws-codepipeline-api/lib/action.ts new file mode 100644 index 0000000000000..f516619ab948c --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-api/lib/action.ts @@ -0,0 +1,313 @@ +import events = require('@aws-cdk/aws-events'); +import iam = require('@aws-cdk/aws-iam'); +import cdk = require('@aws-cdk/cdk'); +import { Artifact } from './artifact'; +import validation = require('./validation'); + +export enum ActionCategory { + Source = 'Source', + Build = 'Build', + Test = 'Test', + Approval = 'Approval', + Deploy = 'Deploy', + Invoke = 'Invoke' +} + +/** + * Specifies the constraints on the number of input and output + * artifacts an action can have. + * + * The constraints for each action type are documented on the + * {@link https://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html Pipeline Structure Reference} page. + */ +export interface ActionArtifactBounds { + readonly minInputs: number; + readonly maxInputs: number; + readonly minOutputs: number; + readonly maxOutputs: number; +} + +export function defaultBounds(): ActionArtifactBounds { + return { + minInputs: 0, + maxInputs: 5, + minOutputs: 0, + maxOutputs: 5 + }; +} + +/** + * The abstract interface of a Pipeline Stage that is used by Actions. + */ +export interface IStage { + /** + * The physical, human-readable name of this Pipeline Stage. + */ + readonly name: string; + + /** + * The ARN of the Pipeline. + */ + readonly pipelineArn: cdk.Arn; + + /** + * The service Role of the Pipeline. + */ + readonly pipelineRole: iam.Role; + + /** + * Grants read & write permissions to the Pipeline's S3 Bucket to the given Identity. + * + * @param identity the IAM Identity to grant the permissions to + */ + grantPipelineBucketReadWrite(identity: iam.IPrincipal): void; + + /** + * Adds an Action to this Stage. + * This is an internal operation - + * an Action is added to a Stage when it's constructed, + * so there's no need to call this method explicitly. + * + * @param action the Action to add to this Stage + */ + _addAction(action: Action): void; +} + +/** + * Common properties shared by all Actions. + */ +export interface CommonActionProps { + /** + * The Pipeline Stage to add this Action to. + */ + stage: IStage; +} + +/** + * Construction properties of the low-level {@link Action Action class}. + */ +export interface ActionProps extends CommonActionProps { + category: ActionCategory; + provider: string; + artifactBounds: ActionArtifactBounds; + configuration?: any; + version?: string; + owner?: string; +} + +/** + * Low-level class for generic CodePipeline Actions. + * It is recommended that concrete types are used instead, such as {@link codecommit.PipelineSource} or + * {@link codebuild.PipelineBuildAction}. + */ +export abstract class Action extends cdk.Construct { + /** + * The category of the action. + * The category defines which action type the owner + * (the entity that performs the action) performs. + */ + public readonly category: ActionCategory; + + /** + * The service provider that the action calls. + */ + public readonly provider: string; + + /** + * The action's configuration. These are key-value pairs that specify input values for an action. + * For more information, see the AWS CodePipeline User Guide. + * + * http://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html#action-requirements + */ + public readonly configuration?: any; + + /** + * The order in which AWS CodePipeline runs this action. + * For more information, see the AWS CodePipeline User Guide. + * + * https://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html#action-requirements + */ + public runOrder: number; + + public readonly owner: string; + public readonly version: string; + + private readonly _inputArtifacts = new Array(); + private readonly _outputArtifacts = new Array(); + private readonly artifactBounds: ActionArtifactBounds; + private readonly stage: IStage; + + constructor(parent: cdk.Construct, id: string, props: ActionProps) { + super(parent, id); + + validation.validateName('Action', id); + + this.owner = props.owner || 'AWS'; + this.version = props.version || '1'; + this.category = props.category; + this.provider = props.provider; + this.configuration = props.configuration; + this.artifactBounds = props.artifactBounds; + this.runOrder = 1; + this.stage = props.stage; + + this.stage._addAction(this); + } + + public validate(): string[] { + return validation.validateArtifactBounds('input', this._inputArtifacts, this.artifactBounds.minInputs, + this.artifactBounds.maxInputs, this.category, this.provider) + .concat(validation.validateArtifactBounds('output', this._outputArtifacts, this.artifactBounds.minOutputs, + this.artifactBounds.maxOutputs, this.category, this.provider) + ); + } + + public onStateChange(name: string, target?: events.IEventRuleTarget, options?: events.EventRuleProps) { + const rule = new events.EventRule(this.parent!!, name, options); + rule.addTarget(target); + rule.addEventPattern({ + detailType: [ 'CodePipeline Stage Execution State Change' ], + source: [ 'aws.codepipeline' ], + resources: [ this.stage.pipelineArn ], + detail: { + stage: [ this.stage.name ], + action: [ this.id ], + }, + }); + return rule; + } + + public get inputArtifacts(): Artifact[] { + return this._inputArtifacts.slice(); + } + + public get outputArtifacts(): Artifact[] { + return this._outputArtifacts.slice(); + } + + protected addChild(child: cdk.Construct, name: string) { + super.addChild(child, name); + if (child instanceof Artifact) { + this._outputArtifacts.push(child); + } + } + + protected addOutputArtifact(name: string): Artifact { + const artifact = new Artifact(this, name); + return artifact; + } + + protected addInputArtifact(artifact: Artifact): Action { + this._inputArtifacts.push(artifact); + return this; + } +} + +// export class TestAction extends Action { +// constructor(parent: Stage, name: string, provider: string, artifactBounds: ActionArtifactBounds, configuration?: any) { +// super(parent, name, { +// category: ActionCategory.Test, +// provider, +// artifactBounds, +// configuration +// }); +// } +// } + +// export class CodeBuildTest extends TestAction { +// constructor(parent: Stage, name: string, project: codebuild.ProjectArnAttribute) { +// super(parent, name, 'CodeBuild', { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 1 }, { +// ProjectName: project +// }); +// } +// } + +// export class CodeDeploy extends DeployAction { +// constructor(parent: Stage, name: string, applicationName: string, deploymentGroupName: string) { +// super(parent, name, 'CodeDeploy', { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 }, { +// ApplicationName: applicationName, +// DeploymentGroupName: deploymentGroupName +// }); +// } +// } + +// export class ElasticBeanstalkDeploy extends DeployAction { +// constructor(parent: Stage, name: string, applicationName: string, environmentName: string) { +// super(parent, name, 'ElasticBeanstalk', { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 }, { +// ApplicationName: applicationName, +// EnvironmentName: environmentName +// }); +// } +// } + +// export class OpsWorksDeploy extends DeployAction { +// constructor(parent: Stage, name: string, app: string, stack: string, layer?: string) { +// super(parent, name, 'OpsWorks', { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 }, { +// Stack: stack, +// App: app, +// Layer: layer, +// }); +// } +// } + +// export class ECSDeploy extends DeployAction { +// constructor(parent: Stage, name: string, clusterName: string, serviceName: string, fileName?: string) { +// super(parent, name, 'ECS', { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 }, { +// ClusterName: clusterName, +// ServiceName: serviceName, +// FileName: fileName, +// }); +// } +// } + +/* + TODO: A Jenkins build needs a corresponding custom action for each "Jenkins provider". + This should be created automatically. + + Example custom action created to execute Jenkins: + { + "id": { + "category": "Test", + "provider": "", + "owner": "Custom", + "version": "1" + }, + "outputArtifactDetails": { + "minimumCount": 0, + "maximumCount": 5 + }, + "settings": { + "executionUrlTemplate": "https://www.google.com/job/{Config:ProjectName}/{ExternalExecutionId}", + "entityUrlTemplate": "https://www.google.com/job/{Config:ProjectName}" + }, + "actionConfigurationProperties": [ + { + "queryable": true, + "key": true, + "name": "ProjectName", + "required": true, + "secret": false + } + ], + "inputArtifactDetails": { + "minimumCount": 0, + "maximumCount": 5 + } + } +*/ + +// export class JenkinsBuild extends BuildAction { +// constructor(parent: Stage, name: string, jenkinsProvider: string, project: string) { +// super(parent, name, jenkinsProvider, DefaultBounds(), { +// ProjectName: project +// }); +// } +// } + +// export class JenkinsTest extends TestAction { +// constructor(parent: Stage, name: string, jenkinsProvider: string, project: string) { +// super(parent, name, jenkinsProvider, DefaultBounds(), { +// ProjectName: project +// }); +// } +// } diff --git a/packages/@aws-cdk/aws-codepipeline/lib/artifact.ts b/packages/@aws-cdk/aws-codepipeline-api/lib/artifact.ts similarity index 98% rename from packages/@aws-cdk/aws-codepipeline/lib/artifact.ts rename to packages/@aws-cdk/aws-codepipeline-api/lib/artifact.ts index eb51d721f96e8..6663e5375effa 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/artifact.ts +++ b/packages/@aws-cdk/aws-codepipeline-api/lib/artifact.ts @@ -1,5 +1,5 @@ import { Construct, Token } from "@aws-cdk/cdk"; -import { Action } from "./actions"; +import { Action } from "./action"; /** * @link https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-parameter-override-functions.html diff --git a/packages/@aws-cdk/aws-codepipeline-api/lib/build-action.ts b/packages/@aws-cdk/aws-codepipeline-api/lib/build-action.ts new file mode 100644 index 0000000000000..65063bf52226b --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-api/lib/build-action.ts @@ -0,0 +1,55 @@ +import cdk = require("@aws-cdk/cdk"); +import { Action, ActionCategory, CommonActionProps } from "./action"; +import { Artifact } from "./artifact"; + +/** + * Construction properties of the low level {@link BuildAction build action}. + */ +export interface BuildActionProps extends CommonActionProps { + /** + * The source to use as input for this build. + */ + inputArtifact: Artifact; + + /** + * The service provider that the action calls. For example, a valid provider for Source actions is CodeBuild. + */ + provider: string; + + /** + * The name of the build's output artifact. + */ + artifactName?: string; + + /** + * The action's configuration. These are key-value pairs that specify input values for an action. + * For more information, see the AWS CodePipeline User Guide. + * + * http://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html#action-requirements + */ + configuration?: any; +} + +/** + * Low level class for build actions. + * It is recommended that concrete types are used instead, + * such as {@link codebuild.PipelineBuildAction}. + */ +export abstract class BuildAction extends Action { + public readonly artifact?: Artifact; + + constructor(parent: cdk.Construct, name: string, props: BuildActionProps) { + super(parent, name, { + stage: props.stage, + artifactBounds: { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 1 }, + category: ActionCategory.Build, + provider: props.provider, + configuration: props.configuration + }); + + this.addInputArtifact(props.inputArtifact); + if (props.artifactName) { + this.artifact = this.addOutputArtifact(props.artifactName); + } + } +} diff --git a/packages/@aws-cdk/aws-codepipeline-api/lib/deploy-action.ts b/packages/@aws-cdk/aws-codepipeline-api/lib/deploy-action.ts new file mode 100644 index 0000000000000..07dad50944857 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-api/lib/deploy-action.ts @@ -0,0 +1,22 @@ +import cdk = require('@aws-cdk/cdk'); +import { Action, ActionArtifactBounds, ActionCategory, CommonActionProps } from "./action"; + +export interface DeployActionProps extends CommonActionProps { + provider: string; + + artifactBounds: ActionArtifactBounds; + + configuration?: any; +} + +export abstract class DeployAction extends Action { + constructor(parent: cdk.Construct, name: string, props: DeployActionProps) { + super(parent, name, { + stage: props.stage, + category: ActionCategory.Deploy, + provider: props.provider, + artifactBounds: props.artifactBounds, + configuration: props.configuration, + }); + } +} diff --git a/packages/@aws-cdk/aws-codepipeline-api/lib/index.ts b/packages/@aws-cdk/aws-codepipeline-api/lib/index.ts new file mode 100644 index 0000000000000..74b5c6eb9d8a4 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-api/lib/index.ts @@ -0,0 +1,6 @@ +export * from './artifact'; +export * from './action'; +export * from './build-action'; +export * from './deploy-action'; +export * from './source-action'; +export * from './validation'; diff --git a/packages/@aws-cdk/aws-codepipeline-api/lib/source-action.ts b/packages/@aws-cdk/aws-codepipeline-api/lib/source-action.ts new file mode 100644 index 0000000000000..3ccea318f59ec --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-api/lib/source-action.ts @@ -0,0 +1,66 @@ +import cdk = require("@aws-cdk/cdk"); +import { Action, ActionCategory, CommonActionProps } from "./action"; +import { Artifact } from "./artifact"; + +/** + * Construction properties of the low-level {@link SourceAction source Action}. + */ +export interface SourceActionProps extends CommonActionProps { + /** + * The source action owner (could be "AWS", "ThirdParty" or "Custom"). + * + * @default "AWS" + */ + owner?: string; + + /** + * The source action verison. + * + * @default "1" + */ + version?: string; + + /** + * The name of the source's output artifact. + * Output artifacts are used by CodePipeline as inputs into other actions. + */ + artifactName: string; + + /** + * The service provider that the action calls. + * For example, a valid provider for Source actions is "S3". + */ + provider: string; + + /** + * The action's configuration. These are key-value pairs that specify input values for an action. + * For more information, see the AWS CodePipeline User Guide. + * + * http://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html#action-requirements + */ + configuration?: any; +} + +/** + * Low-level class for source actions. + * It is recommended that concrete types are used instead, + * such as {@link codepipeline.AmazonS3Source} or + * {@link codecommit.PipelineSource}. + */ +export abstract class SourceAction extends Action { + public readonly artifact: Artifact; + + constructor(parent: cdk.Construct, name: string, props: SourceActionProps) { + super(parent, name, { + stage: props.stage, + category: ActionCategory.Source, + owner: props.owner, + provider: props.provider, + version: props.version, + artifactBounds: { minInputs: 0, maxInputs: 0, minOutputs: 1, maxOutputs: 1 }, + configuration: props.configuration + }); + + this.artifact = this.addOutputArtifact(props.artifactName); + } +} diff --git a/packages/@aws-cdk/aws-codepipeline/lib/validation.ts b/packages/@aws-cdk/aws-codepipeline-api/lib/validation.ts similarity index 97% rename from packages/@aws-cdk/aws-codepipeline/lib/validation.ts rename to packages/@aws-cdk/aws-codepipeline-api/lib/validation.ts index 9769851bed353..2859cb833156d 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/validation.ts +++ b/packages/@aws-cdk/aws-codepipeline-api/lib/validation.ts @@ -1,4 +1,4 @@ -import { ActionCategory } from "./actions"; +import { ActionCategory } from "./action"; import { Artifact } from "./artifact"; /** @@ -47,4 +47,4 @@ export function validateName(thing: string, name: string | undefined) { if (name !== undefined && !VALID_IDENTIFIER_REGEX.test(name)) { throw new Error(`${thing} name must match regular expression: ${VALID_IDENTIFIER_REGEX.toString()}, got '${name}'`); } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codecommit-codepipeline/package.json b/packages/@aws-cdk/aws-codepipeline-api/package.json similarity index 68% rename from packages/@aws-cdk/aws-codecommit-codepipeline/package.json rename to packages/@aws-cdk/aws-codepipeline-api/package.json index ed18e206254de..72ce83307739f 100644 --- a/packages/@aws-cdk/aws-codecommit-codepipeline/package.json +++ b/packages/@aws-cdk/aws-codepipeline-api/package.json @@ -1,17 +1,17 @@ { - "name": "@aws-cdk/aws-codecommit-codepipeline", + "name": "@aws-cdk/aws-codepipeline-api", "version": "0.8.1", - "description": "AWS CodePipline Actions for AWS CodeCommit", + "description": "Actions API for AWS Code Pipeline", "main": "lib/index.js", "types": "lib/index.d.ts", "jsii": { "outdir": "dist", "targets": { "java": { - "package": "software.amazon.awscdk.services.codecommit.codepipeline", + "package": "software.amazon.awscdk.services.codepipeline.api", "maven": { "groupId": "software.amazon.awscdk", - "artifactId": "codecommit-codepipeline" + "artifactId": "codepipeline-api" } }, "sphinx": {} @@ -30,16 +30,14 @@ "pkglint": "pkglint -f", "package": "cdk-package" }, - "nyc": { - "lines": 60, - "branches": 30 - }, "keywords": [ "aws", + "aws-clib", + "aws-cloudlib", "cdk", - "codecommit", + "cloudlib", "codepipeline", - "constructs" + "pipeline" ], "author": { "name": "Amazon Web Services", @@ -54,9 +52,13 @@ "pkglint": "^0.8.1" }, "dependencies": { - "@aws-cdk/aws-codecommit": "^0.8.1", - "@aws-cdk/aws-codepipeline": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-events": "^0.8.1", + "@aws-cdk/aws-iam": "^0.8.1", + "@aws-cdk/cdk": "^0.8.1", + "@aws-cdk/util": "^0.8.1" }, + "bundledDependencies": [ + "@aws-cdk/util" + ], "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-codepipeline/README.md b/packages/@aws-cdk/aws-codepipeline/README.md index c89bf4a6f6351..e37b61d115e03 100644 --- a/packages/@aws-cdk/aws-codepipeline/README.md +++ b/packages/@aws-cdk/aws-codepipeline/README.md @@ -3,7 +3,9 @@ Construct an empty pipeline: ```ts -const pipeline = new Pipeline(this, 'MyFirstPipeline'); +const pipeline = new Pipeline(this, 'MyFirstPipeline', { + pipelineName: 'MyFirstPipeline', +}); ``` All of the components of a pipeline are modeled as constructs. @@ -17,7 +19,8 @@ const sourceStage = new Stage(pipeline, 'Source'); Add an action to a stage: ```ts -new codecommit.PipelineSource(sourceStage, 'source', { +new codecommit.PipelineSource(this, 'Source', { + stage: sourceStage, artifactName: 'MyPackageSourceArtifact', repository: codecommit.RepositoryRef.import(this, 'MyExistingRepository', { repositoryName: new codecommit.RepositoryName('MyExistingRepository'), diff --git a/packages/@aws-cdk/aws-codepipeline/lib/actions.ts b/packages/@aws-cdk/aws-codepipeline/lib/actions.ts deleted file mode 100644 index 805835c43fb8a..0000000000000 --- a/packages/@aws-cdk/aws-codepipeline/lib/actions.ts +++ /dev/null @@ -1,562 +0,0 @@ -import events = require('@aws-cdk/aws-events'); -import s3 = require('@aws-cdk/aws-s3'); -import cdk = require('@aws-cdk/cdk'); -import { Artifact } from './artifact'; -import { cloudformation } from './codepipeline.generated'; -import { Stage } from './stage'; -import validation = require('./validation'); - -export enum ActionCategory { - Source = 'Source', - Build = 'Build', - Test = 'Test', - Approval = 'Approval', - Deploy = 'Deploy', - Invoke = 'Invoke' -} - -/** - * Specifies the constraints on the number of input and output - * artifacts an action can have. - * - * The constraints for each action type are documented on the - * {@link https://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html Pipeline Structure Reference} page. - */ -export interface ActionArtifactBounds { - minInputs: number; - maxInputs: number; - minOutputs: number; - maxOutputs: number; -} - -export function defaultBounds(): ActionArtifactBounds { - return { - minInputs: 0, - maxInputs: 5, - minOutputs: 0, - maxOutputs: 5 - }; -} - -/** - * Construction properties of the low level {@link Action action type}. - */ -export interface ActionProps { - /** - * A category that defines which action type the owner (the entity that performs the action) performs. - * The category that you select determine the providers that you can specify for the {@link #provider} property. - */ - category: ActionCategory; - - /** - * The service provider that the action calls. The providers that you can specify are determined by - * the category that you select. For example, a valid provider for the Deploy category is AWS CodeDeploy, - * which you would specify as CodeDeploy. - */ - provider: string; - - /** - * The constraints to apply to the number of input and output artifacts used by this action. - */ - artifactBounds: ActionArtifactBounds; - - /** - * The action's configuration. These are key-value pairs that specify input values for an action. - * For more information, see the AWS CodePipeline User Guide. - * - * http://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html#action-requirements - */ - configuration?: any; - - /** - * For all currently supported action types, the only valid version string is "1". - * - * @default 1 - */ - version?: string; - - /** - * For all currently supported action types, the only valid owner string is - * "AWS", "ThirdParty", or "Custom". For more information, see the AWS - * CodePipeline API Reference. - * - * @default 'AWS' - */ - owner?: string; -} - -/** - * Low level class for generically creating pipeline actions. - * It is recommended that concrete types are used instead, such as {@link codecommit.PipelineSource} or - * {@link codebuild.PipelineBuildAction}. - */ -export abstract class Action extends cdk.Construct { - /** - * The category of the action. The category defines which action type the owner (the entity that performs the action) performs. - */ - public readonly category: ActionCategory; - - /** - * The service provider that the action calls. - */ - public readonly provider: string; - - /** - * The action's configuration. These are key-value pairs that specify input values for an action. - * For more information, see the AWS CodePipeline User Guide. - * - * http://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html#action-requirements - */ - public readonly configuration?: any; - - /** - * The order in which AWS CodePipeline runs this action. - * For more information, see the AWS CodePipeline User Guide. - * - * https://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html#action-requirements - */ - public runOrder: number; - - private readonly artifactBounds: ActionArtifactBounds; - private readonly inputArtifacts = new Array(); - private readonly outputArtifacts = new Array(); - private readonly owner: string; - private readonly version: string; - private readonly stage: Stage; - - constructor(parent: Stage, name: string, props: ActionProps) { - super(parent, name); - - validation.validateName('Action', name); - - this.owner = props.owner || 'AWS'; - this.version = props.version || '1'; - this.category = props.category; - this.provider = props.provider; - this.configuration = props.configuration; - this.artifactBounds = props.artifactBounds; - this.runOrder = 1; - this.stage = parent; - } - - public validate(): string[] { - return validation.validateArtifactBounds('input', this.inputArtifacts, this.artifactBounds.minInputs, - this.artifactBounds.maxInputs, this.category, this.provider) - .concat(validation.validateArtifactBounds('output', this.outputArtifacts, this.artifactBounds.minOutputs, - this.artifactBounds.maxOutputs, this.category, this.provider) - ); - } - - /** - * Render the Action to a CloudFormation struct - */ - public render(): cloudformation.PipelineResource.ActionDeclarationProperty { - return { - name: this.id, - inputArtifacts: this.inputArtifacts.map(a => ({ name: a.name })), - actionTypeId: { - category: this.category.toString(), - version: this.version, - owner: this.owner, - provider: this.provider, - }, - configuration: this.configuration, - outputArtifacts: this.outputArtifacts.map(a => ({ name: a.name })), - runOrder: this.runOrder - }; - } - - public onStateChange(name: string, target?: events.IEventRuleTarget, options?: events.EventRuleProps) { - const rule = new events.EventRule(this.stage.pipeline, name, options); - rule.addTarget(target); - rule.addEventPattern({ - detailType: [ 'CodePipeline Stage Execution State Change' ], - source: [ 'aws.codepipeline' ], - resources: [ this.stage.pipeline.pipelineArn ], - detail: { - stage: [ this.stage.id ], - action: [ this.id ], - }, - }); - return rule; - } - - /** - * If an Artifact is added as a child, add it to the list of output artifacts. - */ - protected addChild(child: cdk.Construct, name: string) { - super.addChild(child, name); - if (child instanceof Artifact) { - this.outputArtifacts.push(child); - } - } - - protected addOutputArtifact(name: string): Artifact { - const artifact = new Artifact(this, name); - return artifact; - } - - protected addInputArtifact(artifact: Artifact): Action { - this.inputArtifacts.push(artifact); - return this; - } -} - -/** - * Construction properties of the low level {@link Source source action} - */ -export interface SourceProps { - /** - * The source action owner (could e "AWS", "ThirdParty" or "Custom") - * - * @default AWS - */ - owner?: string; - - /** - * The source action verison. - * - * @default "1" - */ - version?: string; - - /** - * The name of the source's output artifact. Output artifacts are used by CodePipeline as - * inputs into other actions. - */ - artifactName: string; - - /** - * The service provider that the action calls. For example, a valid provider for Source actions is S3. - */ - provider: string; - - /** - * The action's configuration. These are key-value pairs that specify input values for an action. - * For more information, see the AWS CodePipeline User Guide. - * - * http://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html#action-requirements - */ - configuration?: any; -} - -/** - * Low level class for source actions. - * It is recommended that concrete types are used instead, such as {@link AmazonS3Source} or - * {@link codecommit.PipelineSource}. - */ -export abstract class Source extends Action { - public readonly artifact: Artifact; - - constructor(parent: Stage, name: string, props: SourceProps) { - super(parent, name, { - category: ActionCategory.Source, - owner: props.owner, - provider: props.provider, - version: props.version, - artifactBounds: { minInputs: 0, maxInputs: 0, minOutputs: 1, maxOutputs: 1 }, - configuration: props.configuration - }); - - this.artifact = this.addOutputArtifact(props.artifactName); - } -} - -/** - * Construction properties of the {@link AmazonS3Source S3 source action} - */ -export interface AmazonS3SourceProps { - /** - * The name of the source's output artifact. Output artifacts are used by CodePipeline as - * inputs into other actions. - */ - artifactName: string; - - /** - * The Amazon S3 bucket that stores the source code - */ - bucket: s3.BucketRef; - - /** - * The key within the S3 bucket that stores the source code - */ - bucketKey: string; - - // TODO: use CloudWatch events instead - /** - * Whether or not AWS CodePipeline should poll for source changes - * - * @default true - */ - pollForSourceChanges?: boolean; -} - -/** - * Source that is provided by a specific Amazon S3 object - */ -export class AmazonS3Source extends Source { - constructor(parent: Stage, name: string, props: AmazonS3SourceProps) { - super(parent, name, { - provider: 'S3', - configuration: { - S3Bucket: props.bucket.bucketName, - S3ObjectKey: props.bucketKey, - PollForSourceChanges: props.pollForSourceChanges || true - }, - artifactName: props.artifactName - }); - - // pipeline needs permissions to read from the S3 bucket - props.bucket.grantRead(parent.pipeline.role); - } -} - -/** - * Construction properties of the {@link GitHubSource GitHub source action} - */ -export interface GithubSourceProps { - /** - * The name of the source's output artifact. Output artifacts are used by CodePipeline as - * inputs into other actions. - */ - artifactName: string; - - /** - * The GitHub account/user that owns the repo. - */ - owner: string; - - /** - * The name of the repo, without the username. - */ - repo: string; - - /** - * The branch to use. - * - * @default "master" - */ - branch?: string; - - /** - * A GitHub OAuth token to use for authentication. - * - * It is recommended to use a `SecretParameter` to obtain the token from the SSM - * Parameter Store: - * - * const oauth = new SecretParameter(this, 'GitHubOAuthToken', { ssmParameter: 'my-github-token }); - * new GitHubSource(stage, 'GH' { oauthToken: oauth }); - * - */ - oauthToken: cdk.Secret; - - /** - * Whether or not AWS CodePipeline should poll for source changes - * - * @default true - */ - pollForSourceChanges?: boolean; -} - -/** - * Source that is provided by a GitHub repository - */ -export class GitHubSource extends Source { - constructor(parent: Stage, name: string, props: GithubSourceProps) { - super(parent, name, { - owner: 'ThirdParty', - provider: 'GitHub', - configuration: { - Owner: props.owner, - Repo: props.repo, - Branch: props.branch || "master", - OAuthToken: props.oauthToken, - PollForSourceChanges: props.pollForSourceChanges || true - }, - artifactName: props.artifactName - }); - } -} - -/** - * Construction properties of the low level {@link BuildAction build action} - */ -export interface BuildActionProps { - /** - * The source to use as input for this build - */ - inputArtifact: Artifact; - - /** - * The service provider that the action calls. For example, a valid provider for Source actions is CodeBuild. - */ - provider: string; - - /** - * The name of the build's output artifact - */ - artifactName?: string; - - /** - * The action's configuration. These are key-value pairs that specify input values for an action. - * For more information, see the AWS CodePipeline User Guide. - * - * http://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html#action-requirements - */ - configuration?: any; -} - -/** - * Low level class for build actions. - * It is recommended that concrete types are used instead, such as {@link codebuild.PipelineBuildAction}. - */ -export abstract class BuildAction extends Action { - public readonly artifact?: Artifact; - - constructor(parent: Stage, name: string, props: BuildActionProps) { - super(parent, name, { - artifactBounds: { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 1 }, - category: ActionCategory.Build, - provider: props.provider, - configuration: props.configuration - }); - - this.addInputArtifact(props.inputArtifact); - if (props.artifactName) { - this.artifact = this.addOutputArtifact(props.artifactName); - } - } -} - -/** - * Manual approval action - */ -export class ApprovalAction extends Action { - constructor(parent: Stage, name: string) { - super(parent, name, { - category: ActionCategory.Approval, - provider: 'Manual', - artifactBounds: { minInputs: 0, maxInputs: 0, minOutputs: 0, maxOutputs: 0 } - }); - } -} - -// export class TestAction extends Action { -// constructor(parent: Stage, name: string, provider: string, artifactBounds: ActionArtifactBounds, configuration?: any) { -// super(parent, name, { -// category: ActionCategory.Test, -// provider, -// artifactBounds, -// configuration -// }); -// } -// } - -// export class CodeBuildTest extends TestAction { -// constructor(parent: Stage, name: string, project: codebuild.ProjectArnAttribute) { -// super(parent, name, 'CodeBuild', { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 1 }, { -// ProjectName: project -// }); -// } -// } - -export class DeployAction extends Action { - constructor(parent: Stage, name: string, provider: string, artifactBounds: ActionArtifactBounds, configuration?: any) { - super(parent, name, { - category: ActionCategory.Deploy, - provider, - artifactBounds, - configuration - }); - } -} - -// export class CodeDeploy extends DeployAction { -// constructor(parent: Stage, name: string, applicationName: string, deploymentGroupName: string) { -// super(parent, name, 'CodeDeploy', { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 }, { -// ApplicationName: applicationName, -// DeploymentGroupName: deploymentGroupName -// }); -// } -// } - -// export class ElasticBeanstalkDeploy extends DeployAction { -// constructor(parent: Stage, name: string, applicationName: string, environmentName: string) { -// super(parent, name, 'ElasticBeanstalk', { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 }, { -// ApplicationName: applicationName, -// EnvironmentName: environmentName -// }); -// } -// } - -// export class OpsWorksDeploy extends DeployAction { -// constructor(parent: Stage, name: string, app: string, stack: string, layer?: string) { -// super(parent, name, 'OpsWorks', { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 }, { -// Stack: stack, -// App: app, -// Layer: layer, -// }); -// } -// } - -// export class ECSDeploy extends DeployAction { -// constructor(parent: Stage, name: string, clusterName: string, serviceName: string, fileName?: string) { -// super(parent, name, 'ECS', { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 }, { -// ClusterName: clusterName, -// ServiceName: serviceName, -// FileName: fileName, -// }); -// } -// } - -/* - TODO: A Jenkins build needs a corresponding custom action for each "Jenkins provider". - This should be created automatically. - - Example custom action created to execute Jenkins: - { - "id": { - "category": "Test", - "provider": "", - "owner": "Custom", - "version": "1" - }, - "outputArtifactDetails": { - "minimumCount": 0, - "maximumCount": 5 - }, - "settings": { - "executionUrlTemplate": "https://www.google.com/job/{Config:ProjectName}/{ExternalExecutionId}", - "entityUrlTemplate": "https://www.google.com/job/{Config:ProjectName}" - }, - "actionConfigurationProperties": [ - { - "queryable": true, - "key": true, - "name": "ProjectName", - "required": true, - "secret": false - } - ], - "inputArtifactDetails": { - "minimumCount": 0, - "maximumCount": 5 - } - } -*/ - -// export class JenkinsBuild extends BuildAction { -// constructor(parent: Stage, name: string, jenkinsProvider: string, project: string) { -// super(parent, name, jenkinsProvider, DefaultBounds(), { -// ProjectName: project -// }); -// } -// } - -// export class JenkinsTest extends TestAction { -// constructor(parent: Stage, name: string, jenkinsProvider: string, project: string) { -// super(parent, name, jenkinsProvider, DefaultBounds(), { -// ProjectName: project -// }); -// } -// } diff --git a/packages/@aws-cdk/aws-codepipeline/lib/github-source-action.ts b/packages/@aws-cdk/aws-codepipeline/lib/github-source-action.ts new file mode 100644 index 0000000000000..037991d92b0a6 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline/lib/github-source-action.ts @@ -0,0 +1,70 @@ +import actions = require('@aws-cdk/aws-codepipeline-api'); +import cdk = require('@aws-cdk/cdk'); + +/** + * Construction properties of the {@link GitHubSource GitHub source action}. + */ +export interface GitHubSourceProps extends actions.CommonActionProps { + /** + * The name of the source's output artifact. Output artifacts are used by CodePipeline as + * inputs into other actions. + */ + artifactName: string; + + /** + * The GitHub account/user that owns the repo. + */ + owner: string; + + /** + * The name of the repo, without the username. + */ + repo: string; + + /** + * The branch to use. + * + * @default "master" + */ + branch?: string; + + /** + * A GitHub OAuth token to use for authentication. + * + * It is recommended to use a `SecretParameter` to obtain the token from the SSM + * Parameter Store: + * + * const oauth = new SecretParameter(this, 'GitHubOAuthToken', { ssmParameter: 'my-github-token }); + * new GitHubSource(stage, 'GH' { oauthToken: oauth }); + * + */ + oauthToken: cdk.Secret; + + /** + * Whether or not AWS CodePipeline should poll for source changes + * + * @default true + */ + pollForSourceChanges?: boolean; +} + +/** + * Source that is provided by a GitHub repository. + */ +export class GitHubSource extends actions.SourceAction { + constructor(parent: cdk.Construct, name: string, props: GitHubSourceProps) { + super(parent, name, { + stage: props.stage, + owner: 'ThirdParty', + provider: 'GitHub', + configuration: { + Owner: props.owner, + Repo: props.repo, + Branch: props.branch || "master", + OAuthToken: props.oauthToken, + PollForSourceChanges: props.pollForSourceChanges || true + }, + artifactName: props.artifactName + }); + } +} diff --git a/packages/@aws-cdk/aws-codepipeline/lib/index.ts b/packages/@aws-cdk/aws-codepipeline/lib/index.ts index afa10c509b913..98b314f4f59aa 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/index.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/index.ts @@ -1,6 +1,7 @@ -export * from './actions'; -export * from './artifact'; +export * from './github-source-action'; +export * from './manual-approval-action'; export * from './pipeline'; +export * from './s3-source-action'; export * from './stage'; // AWS::CodePipeline CloudFormation Resources: diff --git a/packages/@aws-cdk/aws-codepipeline/lib/manual-approval-action.ts b/packages/@aws-cdk/aws-codepipeline/lib/manual-approval-action.ts new file mode 100644 index 0000000000000..f40043d9d05b0 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline/lib/manual-approval-action.ts @@ -0,0 +1,20 @@ +import actions = require('@aws-cdk/aws-codepipeline-api'); +import cdk = require('@aws-cdk/cdk'); + +// tslint:disable-next-line:no-empty-interface +export interface ManualApprovalActionProps extends actions.CommonActionProps { +} + +/** + * Manual approval action. + */ +export class ManualApprovalAction extends actions.Action { + constructor(parent: cdk.Construct, name: string, props: ManualApprovalActionProps) { + super(parent, name, { + stage: props.stage, + category: actions.ActionCategory.Approval, + provider: 'Manual', + artifactBounds: { minInputs: 0, maxInputs: 0, minOutputs: 0, maxOutputs: 0 } + }); + } +} diff --git a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts index 5520436fac94d..29a96cf5c0b7c 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts @@ -1,3 +1,4 @@ +import actions = require('@aws-cdk/aws-codepipeline-api'); import events = require('@aws-cdk/aws-events'); import iam = require('@aws-cdk/aws-iam'); import s3 = require('@aws-cdk/aws-s3'); @@ -5,7 +6,6 @@ import cdk = require('@aws-cdk/cdk'); import util = require('@aws-cdk/util'); import { cloudformation } from './codepipeline.generated'; import { Stage } from './stage'; -import validation = require('./validation'); /** * The ARN of a pipeline @@ -93,7 +93,7 @@ export class Pipeline extends cdk.Construct implements events.IEventRuleTarget { super(parent, name); props = props || {}; - validation.validateName('Pipeline', props.pipelineName); + actions.validateName('Pipeline', props.pipelineName); // If a bucket has been provided, use it - otherwise, create a bucket. let propsBucket = props.artifactBucket; @@ -235,7 +235,7 @@ export class Pipeline extends cdk.Construct implements events.IEventRuleTarget { return util.flatMap(this.stages, (stage, i) => { const onlySourceActionsPermitted = i === 0; return util.flatMap(stage.actions, (action, _) => - validation.validateSourceAction(onlySourceActionsPermitted, action.category, action.id, stage.id) + actions.validateSourceAction(onlySourceActionsPermitted, action.category, action.id, stage.id) ); }); } diff --git a/packages/@aws-cdk/aws-codepipeline/lib/s3-source-action.ts b/packages/@aws-cdk/aws-codepipeline/lib/s3-source-action.ts new file mode 100644 index 0000000000000..d134810d76794 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline/lib/s3-source-action.ts @@ -0,0 +1,53 @@ +import actions = require('@aws-cdk/aws-codepipeline-api'); +import s3 = require('@aws-cdk/aws-s3'); +import cdk = require('@aws-cdk/cdk'); + +/** + * Construction properties of the {@link AmazonS3Source S3 source action}. + */ +export interface AmazonS3SourceProps extends actions.CommonActionProps { + /** + * The name of the source's output artifact. Output artifacts are used by CodePipeline as + * inputs into other actions. + */ + artifactName: string; + + /** + * The Amazon S3 bucket that stores the source code + */ + bucket: s3.BucketRef; + + /** + * The key within the S3 bucket that stores the source code + */ + bucketKey: string; + + // TODO: use CloudWatch events instead + /** + * Whether or not AWS CodePipeline should poll for source changes + * + * @default true + */ + pollForSourceChanges?: boolean; +} + +/** + * Source that is provided by a specific Amazon S3 object. + */ +export class AmazonS3Source extends actions.SourceAction { + constructor(parent: cdk.Construct, name: string, props: AmazonS3SourceProps) { + super(parent, name, { + stage: props.stage, + provider: 'S3', + configuration: { + S3Bucket: props.bucket.bucketName, + S3ObjectKey: props.bucketKey, + PollForSourceChanges: props.pollForSourceChanges || true + }, + artifactName: props.artifactName + }); + + // pipeline needs permissions to read from the S3 bucket + props.bucket.grantRead(props.stage.pipelineRole); + } +} diff --git a/packages/@aws-cdk/aws-codepipeline/lib/stage.ts b/packages/@aws-cdk/aws-codepipeline/lib/stage.ts index 65cbc7ddfc541..8da2ac5d871dd 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/stage.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/stage.ts @@ -1,9 +1,9 @@ +import actions = require('@aws-cdk/aws-codepipeline-api'); import events = require('@aws-cdk/aws-events'); +import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); -import { Action } from './actions'; import { cloudformation } from './codepipeline.generated'; import { Pipeline } from './pipeline'; -import validation = require('./validation'); /** * A stage in a pipeline. Stages are added to a pipeline by constructing a Stage with @@ -13,12 +13,14 @@ import validation = require('./validation'); * // add a stage to a pipeline * new Stage(pipeline, 'MyStage'); */ -export class Stage extends cdk.Construct { +export class Stage extends cdk.Construct implements actions.IStage { /** * The Pipeline this stage is a member of */ public readonly pipeline: Pipeline; - private readonly _actions = new Array(); + public readonly name: string; + + private readonly _actions = new Array(); /** * Append a new stage to the pipeline @@ -29,14 +31,15 @@ export class Stage extends cdk.Construct { */ constructor(parent: Pipeline, name: string) { super(parent, name); + this.name = name; this.pipeline = parent; - validation.validateName('Stage', name); + actions.validateName('Stage', name); } /** * Get a duplicate of this stage's list of actions. */ - public get actions(): Action[] { + public get actions(): actions.Action[] { return this._actions.slice(); } @@ -44,10 +47,14 @@ export class Stage extends cdk.Construct { return this.validateHasActions(); } + public grantPipelineBucketReadWrite(identity: iam.IPrincipal): void { + this.pipeline.artifactBucket.grantReadWrite(identity); + } + public render(): cloudformation.PipelineResource.StageDeclarationProperty { return { name: this.id, - actions: this._actions.map(action => action.render()) + actions: this._actions.map(action => this.renderAction(action)), }; } @@ -65,23 +72,37 @@ export class Stage extends cdk.Construct { return rule; } - /** - * If an action is added as a child, add it to the list of actions. - * TODO: This is a hack that should be removed once the CDK has an - * onChildAdded type hook. - * @override - * @param child - * @param name - */ - protected addChild(child: cdk.Construct, name: string) { - super.addChild(child, name); - if (child instanceof Action) { - this._actions.push(child); - } else { - throw new Error('Only Actions can be added as children to a Stage'); + public get pipelineArn(): cdk.Arn { + return this.pipeline.pipelineArn; + } + + public get pipelineRole(): iam.Role { + return this.pipeline.role; + } + + public _addAction(action: actions.Action): void { + // _addAction should be idempotent in case a customer ever calls it directly + if (!this._actions.includes(action)) { + this._actions.push(action); } } + private renderAction(action: actions.Action): cloudformation.PipelineResource.ActionDeclarationProperty { + return { + name: action.id, + inputArtifacts: action.inputArtifacts.map(a => ({ name: a.name })), + actionTypeId: { + category: action.category.toString(), + version: action.version, + owner: action.owner, + provider: action.provider, + }, + configuration: action.configuration, + outputArtifacts: action.outputArtifacts.map(a => ({ name: a.name })), + runOrder: action.runOrder, + }; + } + private validateHasActions(): string[] { if (this._actions.length === 0) { return [`Stage '${this.id}' must have at least one action`]; diff --git a/packages/@aws-cdk/aws-codepipeline/package.json b/packages/@aws-cdk/aws-codepipeline/package.json index 2f602631be6be..ebc15faf11afc 100644 --- a/packages/@aws-cdk/aws-codepipeline/package.json +++ b/packages/@aws-cdk/aws-codepipeline/package.json @@ -38,10 +38,12 @@ }, "keywords": [ "aws", + "aws-clib", + "aws-cloudlib", "cdk", "cloudlib", - "aws-cloudlib", - "aws-clib" + "codepipeline", + "pipeline" ], "author": { "name": "Amazon Web Services", @@ -51,6 +53,10 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/assert": "^0.8.1", + "@aws-cdk/aws-cloudformation": "^0.8.1", + "@aws-cdk/aws-codebuild": "^0.8.1", + "@aws-cdk/aws-codecommit": "^0.8.1", + "@aws-cdk/aws-lambda": "^0.8.1", "@aws-cdk/aws-sns": "^0.8.1", "cdk-build-tools": "^0.8.1", "cdk-integ-tools": "^0.8.1", @@ -58,6 +64,7 @@ "pkglint": "^0.8.1" }, "dependencies": { + "@aws-cdk/aws-codepipeline-api": "^0.8.1", "@aws-cdk/aws-events": "^0.8.1", "@aws-cdk/aws-iam": "^0.8.1", "@aws-cdk/aws-s3": "^0.8.1", diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/test/integ.template-from-repo.lit.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.cfn-template-from-repo.lit.expected.json similarity index 100% rename from packages/@aws-cdk/aws-cloudformation-codepipeline/test/integ.template-from-repo.lit.expected.json rename to packages/@aws-cdk/aws-codepipeline/test/integ.cfn-template-from-repo.lit.expected.json diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/test/integ.template-from-repo.lit.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.cfn-template-from-repo.lit.ts similarity index 66% rename from packages/@aws-cdk/aws-cloudformation-codepipeline/test/integ.template-from-repo.lit.ts rename to packages/@aws-cdk/aws-codepipeline/test/integ.cfn-template-from-repo.lit.ts index 033c4143c4210..d6587bf2e50a3 100644 --- a/packages/@aws-cdk/aws-cloudformation-codepipeline/test/integ.template-from-repo.lit.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.cfn-template-from-repo.lit.ts @@ -1,8 +1,7 @@ +import cfn = require('@aws-cdk/aws-cloudformation'); import codecommit = require('@aws-cdk/aws-codecommit'); -import codecommitpl = require('@aws-cdk/aws-codecommit-codepipeline'); -import codepipeline = require('@aws-cdk/aws-codepipeline'); import cdk = require('@aws-cdk/cdk'); -import cfnpl = require('../lib'); +import codepipeline = require('../lib'); const app = new cdk.App(process.argv); const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-cloudformation'); @@ -15,7 +14,8 @@ const repo = new codecommit.Repository(stack, 'TemplateRepo', { repositoryName: 'template-repo' }); const sourceStage = new codepipeline.Stage(pipeline, 'Source'); -const source = new codecommitpl.PipelineSource(sourceStage, 'Source', { +const source = new codecommit.PipelineSource(stack, 'Source', { + stage: sourceStage, repository: repo, artifactName: 'SourceArtifact', }); @@ -25,19 +25,23 @@ const prodStage = new codepipeline.Stage(pipeline, 'Deploy'); const stackName = 'OurStack'; const changeSetName = 'StagedChangeSet'; -new cfnpl.CreateReplaceChangeSet(prodStage, 'PrepareChanges', { +new cfn.CreateReplaceChangeSet(prodStage, 'PrepareChanges', { + stage: prodStage, stackName, changeSetName, fullPermissions: true, templatePath: source.artifact.subartifact('template.yaml'), }); -new codepipeline.ApprovalAction(prodStage, 'ApproveChanges'); +new codepipeline.ManualApprovalAction(stack, 'ApproveChanges', { + stage: prodStage, +}); -new cfnpl.ExecuteChangeSet(prodStage, 'ExecuteChanges', { +new cfn.ExecuteChangeSet(stack, 'ExecuteChanges', { + stage: prodStage, stackName, changeSetName, }); /// !hide -process.stdout.write(app.run()); \ No newline at end of file +process.stdout.write(app.run()); diff --git a/packages/@aws-cdk/aws-lambda-codepipeline/test/integ.pipeline.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.expected.json similarity index 100% rename from packages/@aws-cdk/aws-lambda-codepipeline/test/integ.pipeline.expected.json rename to packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.expected.json diff --git a/packages/@aws-cdk/aws-lambda-codepipeline/test/integ.pipeline.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.ts similarity index 79% rename from packages/@aws-cdk/aws-lambda-codepipeline/test/integ.pipeline.ts rename to packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.ts index bdc7199b033f2..2ec41e9e8a5a9 100644 --- a/packages/@aws-cdk/aws-lambda-codepipeline/test/integ.pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.ts @@ -1,8 +1,7 @@ -import codepipeline = require('@aws-cdk/aws-codepipeline'); import lambda = require('@aws-cdk/aws-lambda'); import s3 = require('@aws-cdk/aws-s3'); import cdk = require('@aws-cdk/cdk'); -import lambda_codepipeline = require('../lib'); +import codepipeline = require('../lib'); const app = new cdk.App(process.argv); @@ -14,7 +13,8 @@ const sourceStage = new codepipeline.Stage(pipeline, 'Source'); const bucket = new s3.Bucket(stack, 'PipelineBucket', { versioned: true, }); -new codepipeline.AmazonS3Source(sourceStage, 'Source', { +new codepipeline.AmazonS3Source(stack, 'Source', { + stage: sourceStage, artifactName: 'SourceArtifact', bucket, bucketKey: 'key', @@ -30,7 +30,8 @@ const lambdaFun = new lambda.Function(stack, 'LambdaFun', { runtime: lambda.Runtime.NodeJS610, }); const lambdaStage = new codepipeline.Stage(pipeline, 'Lambda'); -new lambda_codepipeline.PipelineInvokeAction(lambdaStage, 'Lambda', { +new lambda.PipelineInvokeAction(stack, 'Lambda', { + stage: lambdaStage, lambda: lambdaFun, }); diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/test/integ.pipeline.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.expected.json similarity index 100% rename from packages/@aws-cdk/aws-cloudformation-codepipeline/test/integ.pipeline.expected.json rename to packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.expected.json diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/test/integ.pipeline.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.ts similarity index 67% rename from packages/@aws-cdk/aws-cloudformation-codepipeline/test/integ.pipeline.ts rename to packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.ts index cceeb7f2a45a6..6ff220dfc3fe5 100644 --- a/packages/@aws-cdk/aws-cloudformation-codepipeline/test/integ.pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.ts @@ -1,11 +1,10 @@ -import codepipeline = require('@aws-cdk/aws-codepipeline'); -import { ArtifactPath } from '@aws-cdk/aws-codepipeline'; +import cfn = require('@aws-cdk/aws-cloudformation'); +import { ArtifactPath } from '@aws-cdk/aws-codepipeline-api'; import { Role } from '@aws-cdk/aws-iam'; import s3 = require('@aws-cdk/aws-s3'); import cdk = require('@aws-cdk/cdk'); import { ServicePrincipal } from '@aws-cdk/cdk'; - -import cfn_codepipeline = require('../lib'); +import codepipeline = require('../lib'); const app = new cdk.App(process.argv); @@ -17,7 +16,8 @@ const sourceStage = new codepipeline.Stage(pipeline, 'Source'); const bucket = new s3.Bucket(stack, 'PipelineBucket', { versioned: true, }); -const source = new codepipeline.AmazonS3Source(sourceStage, 'Source', { +const source = new codepipeline.AmazonS3Source(stack, 'Source', { + stage: sourceStage, artifactName: 'SourceArtifact', bucket, bucketKey: 'key', @@ -32,11 +32,12 @@ const role = new Role(stack, 'CfnChangeSetRole', { assumedBy: new ServicePrincipal('cloudformation.amazonaws.com'), }); -new cfn_codepipeline.CreateReplaceChangeSet(cfnStage, 'DeployCFN', { - changeSetName, - stackName, - role, - templatePath: new ArtifactPath(source.artifact, 'test.yaml') +new cfn.CreateReplaceChangeSet(stack, 'DeployCFN', { + stage: cfnStage, + changeSetName, + stackName, + role, + templatePath: new ArtifactPath(source.artifact, 'test.yaml') }); process.stdout.write(app.run()); diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-code-commit-build.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.expected.json similarity index 100% rename from packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-code-commit-build.expected.json rename to packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.expected.json diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-code-commit-build.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.ts similarity index 58% rename from packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-code-commit-build.ts rename to packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.ts index d7f40b1f7399b..a1d7a202d6cbc 100644 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-code-commit-build.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.ts @@ -1,32 +1,34 @@ import codebuild = require('@aws-cdk/aws-codebuild'); import codecommit = require('@aws-cdk/aws-codecommit'); -import codecommitPipeline = require('@aws-cdk/aws-codecommit-codepipeline'); -import codepipeline = require('@aws-cdk/aws-codepipeline'); import cdk = require('@aws-cdk/cdk'); -import codebuildPipeline = require('../lib'); +import codepipeline = require('../lib'); const app = new cdk.App(process.argv); const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-codecommit-codebuild'); -const repo = new codecommit.Repository(stack, 'MyRepo', { repositoryName: 'my-repo' }); +const repository = new codecommit.Repository(stack, 'MyRepo', { + repositoryName: 'my-repo', +}); const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); const sourceStage = new codepipeline.Stage(pipeline, 'source'); -const source = new codecommitPipeline.PipelineSource(sourceStage, 'source', { +const source = new codecommit.PipelineSource(stack, 'source', { + stage: sourceStage, artifactName: 'SourceArtifact', - repository: repo, + repository, }); -const buildStage = new codepipeline.Stage(pipeline, 'build'); const project = new codebuild.Project(stack, 'MyBuildProject', { source: new codebuild.CodePipelineSource(), }); -new codebuildPipeline.PipelineBuildAction(buildStage, 'build', { +const buildStage = new codepipeline.Stage(pipeline, 'build'); +new codebuild.PipelineBuildAction(buildStage, 'build', { + stage: buildStage, + inputArtifact: source.artifact, project, - inputArtifact: source.artifact }); process.stdout.write(app.run()); diff --git a/packages/@aws-cdk/aws-codecommit-codepipeline/test/integ.pipeline-code-commit.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit.expected.json similarity index 100% rename from packages/@aws-cdk/aws-codecommit-codepipeline/test/integ.pipeline-code-commit.expected.json rename to packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit.expected.json diff --git a/packages/@aws-cdk/aws-codecommit-codepipeline/test/integ.pipeline-code-commit.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit.ts similarity index 72% rename from packages/@aws-cdk/aws-codecommit-codepipeline/test/integ.pipeline-code-commit.ts rename to packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit.ts index 2ad5b30f01948..967e2acfaad2d 100644 --- a/packages/@aws-cdk/aws-codecommit-codepipeline/test/integ.pipeline-code-commit.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit.ts @@ -1,7 +1,6 @@ import codecommit = require('@aws-cdk/aws-codecommit'); -import codepipeline = require('@aws-cdk/aws-codepipeline'); import cdk = require('@aws-cdk/cdk'); -import { PipelineSource } from '../lib/pipeline-action'; +import codepipeline = require('../lib'); const app = new cdk.App(process.argv); @@ -12,12 +11,15 @@ const repo = new codecommit.Repository(stack, 'MyRepo', { repositoryName: 'my-re const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); const sourceStage = new codepipeline.Stage(pipeline, 'source'); -new PipelineSource(sourceStage, 'source', { +new codecommit.PipelineSource(stack, 'source', { + stage: sourceStage, artifactName: 'SourceArtifact', repository: repo, }); const buildStage = new codepipeline.Stage(pipeline, 'build'); -new codepipeline.ApprovalAction(buildStage, 'manual'); +new codepipeline.ManualApprovalAction(stack, 'manual', { + stage: buildStage, +}); process.stdout.write(app.run()); diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-events.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.expected.json similarity index 100% rename from packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-events.expected.json rename to packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.expected.json diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-events.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.ts similarity index 66% rename from packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-events.ts rename to packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.ts index 19bbe822cc0d5..281517ff303f5 100644 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/test/integ.pipeline-events.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.ts @@ -1,11 +1,10 @@ // Use pipeline as CloudWAtch event target +import codebuild = require('@aws-cdk/aws-codebuild'); import codecommit = require('@aws-cdk/aws-codecommit'); -import codecommitPipeline = require('@aws-cdk/aws-codecommit-codepipeline'); -import codepipeline = require('@aws-cdk/aws-codepipeline'); import sns = require('@aws-cdk/aws-sns'); import cdk = require('@aws-cdk/cdk'); -import codebuildPipeline = require('../lib'); +import codepipeline = require('../lib'); const app = new cdk.App(process.argv); @@ -15,11 +14,21 @@ const pipeline = new codepipeline.Pipeline(stack, 'MyPipeline'); const sourceStage = new codepipeline.Stage(pipeline, 'Source'); const buildStage = new codepipeline.Stage(pipeline, 'Build'); -const repository = new codecommit.Repository(stack, 'CodeCommitRepo', { repositoryName: 'foo' }); -const project = new codebuildPipeline.PipelineProject(stack, 'BuildProject'); +const repository = new codecommit.Repository(stack, 'CodeCommitRepo', { + repositoryName: 'foo' +}); +const project = new codebuild.PipelineProject(stack, 'BuildProject'); -const sourceAction = new codecommitPipeline.PipelineSource(sourceStage, 'CodeCommitSource', { artifactName: 'Source', repository }); -new codebuildPipeline.PipelineBuildAction(buildStage, 'CodeBuildAction', { inputArtifact: sourceAction.artifact, project }); +const sourceAction = new codecommit.PipelineSource(pipeline, 'CodeCommitSource', { + stage: sourceStage, + artifactName: 'Source', + repository, +}); +new codebuild.PipelineBuildAction(stack, 'CodeBuildAction', { + stage: buildStage, + inputArtifact: sourceAction.artifact, + project +}); const topic = new sns.Topic(stack, 'MyTopic'); topic.subscribeEmail('benisrae', 'benisrae@amazon.com'); diff --git a/packages/@aws-cdk/aws-codepipeline/test/test.action.ts b/packages/@aws-cdk/aws-codepipeline/test/test.action.ts index f85d83cade9d6..e1943e9973ec8 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/test.action.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.action.ts @@ -1,11 +1,12 @@ +// import { validateArtifactBounds, validateSourceAction } from '../lib/validation'; +import actions = require('@aws-cdk/aws-codepipeline-api'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; import codepipeline = require('../lib'); -import { validateArtifactBounds, validateSourceAction } from '../lib/validation'; // tslint:disable:object-literal-key-quotes -class TestAction extends codepipeline.Action {} +class TestAction extends actions.Action {} export = { 'artifact bounds validation': { @@ -34,43 +35,46 @@ export = { 'action type validation': { 'must be source and is source'(test: Test) { - const result = validateSourceAction(true, codepipeline.ActionCategory.Source, 'test action', 'test stage'); + const result = actions.validateSourceAction(true, actions.ActionCategory.Source, 'test action', 'test stage'); test.deepEqual(result.length, 0); test.done(); }, 'must be source and is not source'(test: Test) { - const result = validateSourceAction(true, codepipeline.ActionCategory.Deploy, 'test action', 'test stage'); + const result = actions.validateSourceAction(true, actions.ActionCategory.Deploy, 'test action', 'test stage'); test.deepEqual(result.length, 1); test.ok(result[0].match(/may only contain Source actions/), 'the validation should have failed'); test.done(); }, 'cannot be source and is source'(test: Test) { - const result = validateSourceAction(false, codepipeline.ActionCategory.Source, 'test action', 'test stage'); + const result = actions.validateSourceAction(false, actions.ActionCategory.Source, 'test action', 'test stage'); test.deepEqual(result.length, 1); test.ok(result[0].match(/may only occur in first stage/), 'the validation should have failed'); test.done(); }, 'cannot be source and is not source'(test: Test) { - const result = validateSourceAction(false, codepipeline.ActionCategory.Deploy, 'test action', 'test stage'); + const result = actions.validateSourceAction(false, actions.ActionCategory.Deploy, 'test action', 'test stage'); test.deepEqual(result.length, 0); test.done(); }, }, 'standard action with artifacts'(test: Test) { - const stage = stageForTesting(); - const action = new TestAction(stage, 'TestAction', { - artifactBounds: defaultBounds(), - category: codepipeline.ActionCategory.Source, + const stack = new cdk.Stack(); + const pipeline = new codepipeline.Pipeline(stack, 'pipeline'); + const stage = new codepipeline.Stage(pipeline, 'stage'); + const action = new TestAction(stack, 'TestAction', { + stage, + artifactBounds: actions.defaultBounds(), + category: actions.ActionCategory.Source, provider: 'test provider', configuration: { blah: 'bleep' } }); - new codepipeline.Artifact(action, 'TestOutput'); + new actions.Artifact(action, 'TestOutput'); - test.deepEqual(action.render(), { + test.deepEqual((stage.render().actions as any)[0], { name: 'TestAction', inputArtifacts: [], actionTypeId: @@ -89,30 +93,18 @@ export = { }; function boundsValidationResult(numberOfArtifacts: number, min: number, max: number): string[] { - const stage = stageForTesting(); - const action = new TestAction(stage, 'TestAction', { - artifactBounds: defaultBounds(), - category: codepipeline.ActionCategory.Test, + const stack = new cdk.Stack(); + const pipeline = new codepipeline.Pipeline(stack, 'pipeline'); + const stage = new codepipeline.Stage(pipeline, 'stage'); + const action = new TestAction(stack, 'TestAction', { + stage, + artifactBounds: actions.defaultBounds(), + category: actions.ActionCategory.Test, provider: 'test provider' }); - const artifacts: codepipeline.Artifact[] = []; + const artifacts: actions.Artifact[] = []; for (let i = 0; i < numberOfArtifacts; i++) { - artifacts.push(new codepipeline.Artifact(action, `TestArtifact${i}`)); + artifacts.push(new actions.Artifact(action, `TestArtifact${i}`)); } - return validateArtifactBounds('output', artifacts, min, max, 'testCategory', 'testProvider'); -} - -function stageForTesting(): codepipeline.Stage { - const stack = new cdk.Stack(); - const pipeline = new codepipeline.Pipeline(stack, 'pipeline'); - return new codepipeline.Stage(pipeline, 'stage'); -} - -function defaultBounds(): codepipeline.ActionArtifactBounds { - return { - minInputs: 0, - maxInputs: 5, - minOutputs: 0, - maxOutputs: 5 - }; + return actions.validateArtifactBounds('output', artifacts, min, max, 'testCategory', 'testProvider'); } diff --git a/packages/@aws-cdk/aws-cloudformation-codepipeline/test/test.pipeline-actions.ts b/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts similarity index 90% rename from packages/@aws-cdk/aws-cloudformation-codepipeline/test/test.pipeline-actions.ts rename to packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts index 51d6f0bfc41d6..3ce276d0f9542 100644 --- a/packages/@aws-cdk/aws-cloudformation-codepipeline/test/test.pipeline-actions.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts @@ -1,14 +1,13 @@ import { expect, haveResource } from '@aws-cdk/assert'; -import { CodePipelineBuildArtifacts, CodePipelineSource, Project } from '@aws-cdk/aws-codebuild'; -import { PipelineBuildAction } from '@aws-cdk/aws-codebuild-codepipeline'; -import { Repository } from '@aws-cdk/aws-codecommit'; -import { PipelineSource } from '@aws-cdk/aws-codecommit-codepipeline'; -import { ArtifactPath, Pipeline, Stage } from '@aws-cdk/aws-codepipeline'; +import { CreateReplaceChangeSet, CreateUpdateStack, ExecuteChangeSet } from '@aws-cdk/aws-cloudformation'; +import { CodePipelineBuildArtifacts, CodePipelineSource, PipelineBuildAction, Project } from '@aws-cdk/aws-codebuild'; +import { PipelineSource, Repository } from '@aws-cdk/aws-codecommit'; +import { ArtifactPath } from '@aws-cdk/aws-codepipeline-api'; import { Role } from '@aws-cdk/aws-iam'; import cdk = require('@aws-cdk/cdk'); import { PolicyStatement, ServicePrincipal } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; -import { CreateReplaceChangeSet, CreateUpdateStack, ExecuteChangeSet } from '../lib/pipeline-actions'; +import { Pipeline, Stage } from '../lib'; // tslint:disable:object-literal-key-quotes @@ -27,7 +26,8 @@ export = { const sourceStage = new Stage(pipeline, 'source'); - const source = new PipelineSource(sourceStage, 'source', { + const source = new PipelineSource(stack, 'source', { + stage: sourceStage, artifactName: 'SourceArtifact', repository: repo, }); @@ -41,7 +41,8 @@ export = { artifacts: buildArtifacts, }); - const buildAction = new PipelineBuildAction(buildStage, 'build', { + const buildAction = new PipelineBuildAction(stack, 'build', { + stage: buildStage, project, inputArtifact: source.artifact, artifactName: "OutputYo" @@ -56,14 +57,16 @@ export = { const stackName = 'BrelandsStack'; const changeSetName = 'MyMagicalChangeSet'; - new CreateReplaceChangeSet(prodStage, 'BuildChangeSetProd', { + new CreateReplaceChangeSet(stack, 'BuildChangeSetProd', { + stage: prodStage, stackName, changeSetName, role: changeSetExecRole, templatePath: new ArtifactPath(buildAction.artifact!, 'template.yaml'), }); - new ExecuteChangeSet(prodStage, 'ExecuteChangeSetProd', { + new ExecuteChangeSet(stack, 'ExecuteChangeSetProd', { + stage: prodStage, stackName, changeSetName, }); @@ -196,6 +199,7 @@ export = { // WHEN new CreateUpdateStack(stack.deployStage, 'CreateUpdate', { + stage: stack.deployStage, stackName: 'MyStack', templatePath: stack.source.artifact.subartifact('template.yaml'), fullPermissions: true, @@ -247,7 +251,8 @@ export = { const stack = new TestFixture(); // WHEN - new CreateUpdateStack(stack.deployStage, 'CreateUpdate', { + new CreateUpdateStack(stack, 'CreateUpdate', { + stage: stack.deployStage, stackName: 'MyStack', templatePath: stack.source.artifact.subartifact('template.yaml'), outputFileName: 'CreateResponse.json', @@ -277,7 +282,8 @@ export = { const stack = new TestFixture(); // WHEN - new CreateUpdateStack(stack.deployStage, 'CreateUpdate', { + new CreateUpdateStack(stack, 'CreateUpdate', { + stage: stack.deployStage, stackName: 'MyStack', templatePath: stack.source.artifact.subartifact('template.yaml'), replaceOnFailure: true, @@ -322,7 +328,8 @@ class TestFixture extends cdk.Stack { this.sourceStage = new Stage(this.pipeline, 'Source'); this.deployStage = new Stage(this.pipeline, 'Deploy'); this.repo = new Repository(this, 'MyVeryImportantRepo', { repositoryName: 'my-very-important-repo' }); - this.source = new PipelineSource(this.sourceStage, 'Source', { + this.source = new PipelineSource(this, 'Source', { + stage: this.sourceStage, artifactName: 'SourceArtifact', repository: this.repo, }); diff --git a/packages/@aws-cdk/aws-codepipeline/test/test.general-validation.ts b/packages/@aws-cdk/aws-codepipeline/test/test.general-validation.ts index 04c1bb610729e..655cc7d555792 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/test.general-validation.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.general-validation.ts @@ -1,10 +1,10 @@ +import actions = require('@aws-cdk/aws-codepipeline-api'); import s3 = require('@aws-cdk/aws-s3'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; -import { AmazonS3Source } from '../lib/actions'; import { Pipeline } from '../lib/pipeline'; +import { AmazonS3Source } from '../lib/s3-source-action'; import { Stage } from '../lib/stage'; -import { validateName } from '../lib/validation'; interface NameValidationTestCase { name: string; @@ -23,7 +23,7 @@ export = { cases.forEach(testCase => { const name = testCase.name; - const validationBlock = () => { validateName('test thing', name); }; + const validationBlock = () => { actions.validateName('test thing', name); }; if (testCase.shouldPassValidation) { test.doesNotThrow(validationBlock, Error, `${name} failed validation but ${testCase.explanation}`); } else { @@ -61,12 +61,14 @@ export = { const secondStage = new Stage(pipeline, 'SecondStage'); const bucket = new s3.Bucket(stack, 'PipelineBucket'); - new AmazonS3Source(firstStage, 'FirstAction', { + new AmazonS3Source(stack, 'FirstAction', { + stage: firstStage, artifactName: 'FirstArtifact', bucket, bucketKey: 'key', }); - new AmazonS3Source(secondStage, 'SecondAction', { + new AmazonS3Source(stack, 'SecondAction', { + stage: secondStage, artifactName: 'SecondAction', bucket, bucketKey: 'key', diff --git a/packages/@aws-cdk/aws-codebuild-codepipeline/test/test.pipeline.ts b/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts similarity index 56% rename from packages/@aws-cdk/aws-codebuild-codepipeline/test/test.pipeline.ts rename to packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts index 7ddcfa8b706f8..71606d3ca6df1 100644 --- a/packages/@aws-cdk/aws-codebuild-codepipeline/test/test.pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts @@ -1,38 +1,39 @@ import { expect, haveResource } from '@aws-cdk/assert'; import codebuild = require('@aws-cdk/aws-codebuild'); import codecommit = require('@aws-cdk/aws-codecommit'); -import codecommitPipeline = require('@aws-cdk/aws-codecommit-codepipeline'); -import codepipeline = require('@aws-cdk/aws-codepipeline'); +import lambda = require('@aws-cdk/aws-lambda'); import s3 = require('@aws-cdk/aws-s3'); import sns = require('@aws-cdk/aws-sns'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; -import codebuildPipeline = require('../lib'); +import codepipeline = require('../lib'); // tslint:disable:object-literal-key-quotes export = { 'basic pipeline'(test: Test) { - const stack = new cdk.Stack(); - const repo = new codecommit.Repository(stack, 'MyRepo', { repositoryName: 'my-repo' }); + const repository = new codecommit.Repository(stack, 'MyRepo', { + repositoryName: 'my-repo', + }); const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); const sourceStage = new codepipeline.Stage(pipeline, 'source'); - const source = new codecommitPipeline.PipelineSource(sourceStage, 'source', { + const source = new codecommit.PipelineSource(stack, 'source', { + stage: sourceStage, artifactName: 'SourceArtifact', - repository: repo, + repository, }); const buildStage = new codepipeline.Stage(pipeline, 'build'); const project = new codebuild.Project(stack, 'MyBuildProject', { source: new codebuild.CodePipelineSource() }); - - new codebuildPipeline.PipelineBuildAction(buildStage, 'build', { + new codebuild.PipelineBuildAction(stack, 'build', { + stage: buildStage, + inputArtifact: source.artifact, project, - inputArtifact: source.artifact }); test.notDeepEqual(stack.toCloudFormation(), {}); @@ -47,7 +48,9 @@ export = { const p = new codepipeline.Pipeline(stack, 'P'); - new codepipeline.GitHubSource(new codepipeline.Stage(p, 'Source'), 'GH', { + const s1 = new codepipeline.Stage(p, 'Source'); + new codepipeline.GitHubSource(stack, 'GH', { + stage: s1, artifactName: 'A', branch: 'branch', oauthToken: secret, @@ -55,7 +58,8 @@ export = { repo: 'bar' }); - new codepipeline.ApprovalAction(new codepipeline.Stage(p, 'Two'), 'Boo'); + const s2 = new codepipeline.Stage(p, 'Two'); + new codepipeline.ManualApprovalAction(stack, 'Boo', { stage: s2 }); expect(stack).to(haveResource('AWS::CodePipeline::Pipeline', { "ArtifactStore": { @@ -132,12 +136,16 @@ export = { const pipeline = new codepipeline.Pipeline(stack, 'PL'); - new codepipeline.AmazonS3Source(new codepipeline.Stage(pipeline, 'S1'), 'A1', { + const stage1 = new codepipeline.Stage(pipeline, 'S1'); + new codepipeline.AmazonS3Source(stack, 'A1', { + stage: stage1, artifactName: 'Artifact', bucket: new s3.Bucket(stack, 'Bucket'), bucketKey: 'Key' }); - new codepipeline.ApprovalAction(new codepipeline.Stage(pipeline, 'S2'), 'A2'); + + const stage2 = new codepipeline.Stage(pipeline, 'S2'); + new codepipeline.ManualApprovalAction(stack, 'A2', { stage: stage2 }); pipeline.onStateChange('OnStateChange', topic, { description: 'desc', @@ -213,7 +221,7 @@ export = { 'sets the source and artifacts to CodePipeline'(test: Test) { const stack = new cdk.Stack(); - new codebuildPipeline.PipelineProject(stack, 'MyProject', { + new codebuild.PipelineProject(stack, 'MyProject', { projectName: 'MyProject', }); @@ -243,4 +251,125 @@ export = { } } }, + + 'Lambda PipelineInvokeAction can be used to invoke Lambda functions from a CodePipeline'(test: Test) { + const stack = new cdk.Stack(); + + const lambdaFun = new lambda.Function(stack, 'Function', { + code: new lambda.InlineCode('bla'), + handler: 'index.handler', + runtime: lambda.Runtime.NodeJS43, + }); + + const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); + const stage = new codepipeline.Stage(pipeline, 'Stage'); + new lambda.PipelineInvokeAction(stack, 'InvokeAction', { + stage, + lambda: lambdaFun, + userParameters: 'foo-bar/42' + }); + + expect(stack).to(haveResource('AWS::CodePipeline::Pipeline', { + "ArtifactStore": { + "Location": { + "Ref": "PipelineArtifactsBucket22248F97" + }, + "Type": "S3" + }, + "RoleArn": { + "Fn::GetAtt": [ + "PipelineRoleD68726F7", + "Arn" + ] + }, + "Stages": [ + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Invoke", + "Owner": "AWS", + "Provider": "Lambda", + "Version": "1" + }, + "Configuration": { + "FunctionName": { + "Ref": "Function76856677" + }, + "UserParameters": "foo-bar/42" + }, + "InputArtifacts": [], + "Name": "InvokeAction", + "OutputArtifacts": [], + "RunOrder": 1 + } + ], + "Name": "Stage" + } + ] + })); + + expect(stack).to(haveResource('AWS::IAM::Policy', { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "codepipeline:PutJobSuccessResult", + "codepipeline:PutJobFailureResult" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "FunctionServiceRoleDefaultPolicy2F49994A", + "Roles": [ + { + "Ref": "FunctionServiceRole675BB04A" + } + ] + })); + + test.done(); + }, + + 'polling for changes': { + 'does not poll for changes'(test: Test) { + const stack = new cdk.Stack(); + + const result = new codecommit.PipelineSource(stack, 'stage', { + stage: stageForTesting(stack), + artifactName: 'SomeArtifact', + repository: repositoryForTesting(stack), + pollForSourceChanges: false, + }); + test.equal(result.configuration.PollForSourceChanges, false); + test.done(); + }, + + 'polls for changes'(test: Test) { + const stack = new cdk.Stack(); + + const result = new codecommit.PipelineSource(stack, 'stage', { + stage: stageForTesting(stack), + artifactName: 'SomeArtifact', + repository: repositoryForTesting(stack), + pollForSourceChanges: true, + }); + test.equal(result.configuration.PollForSourceChanges, true); + test.done(); + } + } }; + +function stageForTesting(stack: cdk.Stack): codepipeline.Stage { + const pipeline = new codepipeline.Pipeline(stack, 'pipeline'); + return new codepipeline.Stage(pipeline, 'stage'); +} + +function repositoryForTesting(stack: cdk.Stack): codecommit.Repository { + return new codecommit.Repository(stack, 'Repository', { + repositoryName: 'Repository' + }); +} diff --git a/packages/@aws-cdk/aws-lambda-codepipeline/.gitignore b/packages/@aws-cdk/aws-lambda-codepipeline/.gitignore deleted file mode 100644 index 95b285093bf48..0000000000000 --- a/packages/@aws-cdk/aws-lambda-codepipeline/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -*.js -*.js.map -*.d.ts -node_modules -*.generated.ts -dist -tsconfig.json -tslint.json - -.jsii - -.LAST_BUILD -.nyc_output -coverage -.nycrc -.LAST_PACKAGE \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-codepipeline/LICENSE b/packages/@aws-cdk/aws-lambda-codepipeline/LICENSE deleted file mode 100644 index 1739faaebb745..0000000000000 --- a/packages/@aws-cdk/aws-lambda-codepipeline/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/packages/@aws-cdk/aws-lambda-codepipeline/NOTICE b/packages/@aws-cdk/aws-lambda-codepipeline/NOTICE deleted file mode 100644 index 95fd48569c743..0000000000000 --- a/packages/@aws-cdk/aws-lambda-codepipeline/NOTICE +++ /dev/null @@ -1,2 +0,0 @@ -AWS Cloud Development Kit (AWS CDK) -Copyright 2018-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-lambda-codepipeline/README.md b/packages/@aws-cdk/aws-lambda-codepipeline/README.md deleted file mode 100644 index 1a16b93ca42a0..0000000000000 --- a/packages/@aws-cdk/aws-lambda-codepipeline/README.md +++ /dev/null @@ -1,24 +0,0 @@ -## AWS CodePipline Actions for AWS Lambda - -This module contains an Action that allows you to invoke a Lambda function from CodePipeline. - -Example usage: - -```ts -import codepipeline = require('@aws-cdk/aws-codepipeline'); -import lambda = require('@aws-cdk/aws-lambda'); -import lambdaCodepipeline = require('@aws-cdk/aws-lambda-codepipeline'); - -// see the @aws-cdk/aws-lambda module for more documentation on how to create Lamda functions -const lambdaFun = new lambda.Lambda(// ... -); - -const pipeline = new codepipeline.Pipeline(this, 'MyPipeline'); -const lambdaStage = new codepipeline.Stage(pipeline, 'Lambda'); -new lambdaCodepipeline.PipelineInvokeAction(lambdaStage, 'Lambda', { - lambda: lambdaFun, -}); -``` - -See [the AWS documentation](https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html) -on how to write a Lambda function invoked from CodePipeline. diff --git a/packages/@aws-cdk/aws-lambda-codepipeline/lib/index.ts b/packages/@aws-cdk/aws-lambda-codepipeline/lib/index.ts deleted file mode 100644 index 890162998e10d..0000000000000 --- a/packages/@aws-cdk/aws-lambda-codepipeline/lib/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './pipeline-action'; diff --git a/packages/@aws-cdk/aws-lambda-codepipeline/package.json b/packages/@aws-cdk/aws-lambda-codepipeline/package.json deleted file mode 100644 index b041462565908..0000000000000 --- a/packages/@aws-cdk/aws-lambda-codepipeline/package.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "name": "@aws-cdk/aws-lambda-codepipeline", - "version": "0.8.1", - "description": "AWS CodePipline Actions for AWS Lambda", - "main": "lib/index.js", - "types": "lib/index.d.ts", - "jsii": { - "outdir": "dist", - "targets": { - "java": { - "package": "software.amazon.awscdk.services.lambda.codepipeline", - "maven": { - "groupId": "software.amazon.awscdk", - "artifactId": "lambda-codepipeline" - } - }, - "sphinx": {} - } - }, - "repository": { - "type": "git", - "url": "https://github.com/awslabs/aws-cdk.git" - }, - "scripts": { - "build": "cdk-build", - "watch": "cdk-watch", - "lint": "cdk-lint", - "test": "cdk-test", - "integ": "cdk-integ", - "pkglint": "pkglint -f", - "package": "cdk-package" - }, - "nyc": { - "lines": 60, - "branches": 30 - }, - "keywords": [ - "aws", - "cdk", - "codepipeline", - "constructs", - "lambda" - ], - "author": { - "name": "Amazon Web Services", - "url": "https://aws.amazon.com", - "organization": true - }, - "license": "Apache-2.0", - "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "pkglint": "^0.8.1" - }, - "dependencies": { - "@aws-cdk/aws-codepipeline": "^0.8.1", - "@aws-cdk/aws-lambda": "^0.8.1", - "@aws-cdk/aws-s3": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" - }, - "homepage": "https://github.com/awslabs/aws-cdk" -} diff --git a/packages/@aws-cdk/aws-lambda-codepipeline/test/test.pipeline-action.ts b/packages/@aws-cdk/aws-lambda-codepipeline/test/test.pipeline-action.ts deleted file mode 100644 index b9513222f8827..0000000000000 --- a/packages/@aws-cdk/aws-lambda-codepipeline/test/test.pipeline-action.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { expect, haveResource } from '@aws-cdk/assert'; -import codepipeline = require('@aws-cdk/aws-codepipeline'); -import lambda = require('@aws-cdk/aws-lambda'); -import cdk = require('@aws-cdk/cdk'); -import { Test } from 'nodeunit'; -import { PipelineInvokeAction } from '../lib/pipeline-action'; - -// tslint:disable:object-literal-key-quotes - -export = { - 'PipelineInvokeAction can be used to invoke lambda functions from a CodePipeline'(test: Test) { - const stack = new cdk.Stack(); - - const lambdaFun = new lambda.Function(stack, 'Function', { - code: new lambda.InlineCode('bla'), - handler: 'index.handler', - runtime: lambda.Runtime.NodeJS43, - }); - - const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); - - new PipelineInvokeAction(new codepipeline.Stage(pipeline, 'Stage'), 'InvokeAction', { - lambda: lambdaFun, - userParameters: 'foo-bar/42' - }); - - expect(stack).to(haveResource('AWS::CodePipeline::Pipeline', { - "ArtifactStore": { - "Location": { - "Ref": "PipelineArtifactsBucket22248F97" - }, - "Type": "S3" - }, - "RoleArn": { - "Fn::GetAtt": [ - "PipelineRoleD68726F7", - "Arn" - ] - }, - "Stages": [ - { - "Actions": [ - { - "ActionTypeId": { - "Category": "Invoke", - "Owner": "AWS", - "Provider": "Lambda", - "Version": "1" - }, - "Configuration": { - "FunctionName": { - "Ref": "Function76856677" - }, - "UserParameters": "foo-bar/42" - }, - "InputArtifacts": [], - "Name": "InvokeAction", - "OutputArtifacts": [], - "RunOrder": 1 - } - ], - "Name": "Stage" - } - ] - })); - - expect(stack).to(haveResource('AWS::IAM::Policy', { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "codepipeline:PutJobSuccessResult", - "codepipeline:PutJobFailureResult" - ], - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "FunctionServiceRoleDefaultPolicy2F49994A", - "Roles": [ - { - "Ref": "FunctionServiceRole675BB04A" - } - ] - })); - - test.done(); - } -}; diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index 78d22f32f2cc5..759f22beadf86 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -35,3 +35,26 @@ local directory `my-lambda-handler` to it: When deploying a stack that contains this code, the directory will be zip archived and then uploaded to an S3 bucket, then the exact location of the S3 objects will be passed when the stack is deployed. + +### Lambda in CodePipeline + +This module also contains an Action that allows you to invoke a Lambda function from CodePipeline: + +```ts +import codepipeline = require('@aws-cdk/aws-codepipeline'); +import lambda = require('@aws-cdk/aws-lambda'); + +const lambdaFun = new lambda.Function(this, 'MyLambda', { + // some lambda parameters here... +}); + +const pipeline = new codepipeline.Pipeline(this, 'MyPipeline'); +const lambdaStage = new codepipeline.Stage(pipeline, 'Lambda'); +new lambda.PipelineInvokeAction(this, 'Lambda', { + stage: lambdaStage, + lambda: lambdaFun, +}); +``` + +See [the AWS documentation](https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html) +on how to write a Lambda function invoked from CodePipeline. diff --git a/packages/@aws-cdk/aws-lambda/lib/index.ts b/packages/@aws-cdk/aws-lambda/lib/index.ts index 3593e78c670f8..c512f7fc0c1ef 100644 --- a/packages/@aws-cdk/aws-lambda/lib/index.ts +++ b/packages/@aws-cdk/aws-lambda/lib/index.ts @@ -2,6 +2,7 @@ export * from './alias'; export * from './lambda-ref'; export * from './lambda'; export * from './permission'; +export * from './pipeline-action'; export * from './runtime'; export * from './code'; export * from './inline'; diff --git a/packages/@aws-cdk/aws-lambda-codepipeline/lib/pipeline-action.ts b/packages/@aws-cdk/aws-lambda/lib/pipeline-action.ts similarity index 85% rename from packages/@aws-cdk/aws-lambda-codepipeline/lib/pipeline-action.ts rename to packages/@aws-cdk/aws-lambda/lib/pipeline-action.ts index a2088ecf6c308..fa3789bd719eb 100644 --- a/packages/@aws-cdk/aws-lambda-codepipeline/lib/pipeline-action.ts +++ b/packages/@aws-cdk/aws-lambda/lib/pipeline-action.ts @@ -1,15 +1,15 @@ -import codepipeline = require('@aws-cdk/aws-codepipeline'); -import lambda = require('@aws-cdk/aws-lambda'); +import codepipeline = require('@aws-cdk/aws-codepipeline-api'); import cdk = require('@aws-cdk/cdk'); +import { FunctionRef } from './lambda-ref'; /** * Construction properties of the {@link PipelineInvokeAction Lambda invoke CodePipeline Action}. */ -export interface PipelineInvokeActionProps { +export interface PipelineInvokeActionProps extends codepipeline.CommonActionProps { /** * The lambda function to invoke. */ - lambda: lambda.FunctionRef; + lambda: FunctionRef; /** * String to be used in the event data parameter passed to the Lambda @@ -45,8 +45,9 @@ export interface PipelineInvokeActionProps { * @see https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html */ export class PipelineInvokeAction extends codepipeline.Action { - constructor(parent: codepipeline.Stage, name: string, props: PipelineInvokeActionProps) { + constructor(parent: cdk.Construct, name: string, props: PipelineInvokeActionProps) { super(parent, name, { + stage: props.stage, category: codepipeline.ActionCategory.Invoke, provider: 'Lambda', artifactBounds: codepipeline.defaultBounds(), @@ -57,12 +58,12 @@ export class PipelineInvokeAction extends codepipeline.Action { }); // allow pipeline to list functions - parent.pipeline.addToRolePolicy(new cdk.PolicyStatement() + props.stage.pipelineRole.addToPolicy(new cdk.PolicyStatement() .addAction('lambda:ListFunctions') .addResource('*')); // allow pipeline to invoke this lambda functionn - parent.pipeline.addToRolePolicy(new cdk.PolicyStatement() + props.stage.pipelineRole.addToPolicy(new cdk.PolicyStatement() .addAction('lambda:InvokeFunction') .addResource(props.lambda.functionArn)); diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 5a108e658cdcb..5a30b3a292d1d 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -33,6 +33,9 @@ "cdk-build": { "cloudformation": "AWS::Lambda" }, + "nyc": { + "lines": 75 + }, "keywords": [ "aws", "cdk", @@ -55,6 +58,7 @@ "dependencies": { "@aws-cdk/assets": "^0.8.1", "@aws-cdk/aws-cloudwatch": "^0.8.1", + "@aws-cdk/aws-codepipeline-api": "^0.8.1", "@aws-cdk/aws-events": "^0.8.1", "@aws-cdk/aws-iam": "^0.8.1", "@aws-cdk/aws-logs": "^0.8.1", From fbbe91321878232c9f71d3171cd5a3fc9f582834 Mon Sep 17 00:00:00 2001 From: Doug-AWS Date: Tue, 14 Aug 2018 21:25:16 -0700 Subject: [PATCH 19/33] Deleted erroneous file --- packages/aws-cdk-docs/src/howto.rst | 124 ---------------------------- 1 file changed, 124 deletions(-) delete mode 100644 packages/aws-cdk-docs/src/howto.rst diff --git a/packages/aws-cdk-docs/src/howto.rst b/packages/aws-cdk-docs/src/howto.rst deleted file mode 100644 index 21bf4c79d137b..0000000000000 --- a/packages/aws-cdk-docs/src/howto.rst +++ /dev/null @@ -1,124 +0,0 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file 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. - -.. note:: These instructions are only for the Amazon-internal preview of the |cdk|. - -.. _how_to: - -###################################### -Performing Common Tasks With the |cdk| -###################################### - -This topic describes how to perform some common tasks with the |cdk|. -Each section starts with the question "How do I" and supplies an answer. -Note that there are often multiple ways of performing a task. -This topic describes the simplest, most straightforward way. - -.. _how_to_use_cfn_template: - -How do I use an Existing |CFN| Template? -======================================== - -The |cdk| provides a mechanism that you can use in your code to include an -existing |CFN| template in your app. - -The following example includes the template *my-template.json* into the -existing app and gets the ARN of the bucket **mybucket** from the -template. - -.. code-block:: js - - import { FnGetAtt } from '@aws-cdk/core' - import { readFileSync } from 'fs' - - new Include(this, 'ExistingInfra', { - template: JSON.parse(readFileSync('my-template.json').toString)) - }); - - // To get the bucket's ARN: - const bucketArn = new FnGetAtt('mybucket', 'Arn'); - -The *my-template.json' must have the following resource, -where **abcdwxyz** is the unique, 8-character hash that the |cdk| generates for the resource: - -.. code-block:: json - - "TheBucketabcdwxyz": { - "Type": "AWS::S3::Bucket", - "Properties": { - "BucketName": "mybucket" - } - } - -.. _how_to_add_a_stack: - -How do I Add a Stack to My App? -=============================== - -To add a stack to your app, -create a new class that extends the -::py:class:`_aws-cdk_core.Stack` class, -as shown in the following example. - -.. code-block:: js - - import { Stack, StackProps } from '@aws-cdk/core'; - // ... - class HelloStack extends Stack { - constructor(parent: App, name: string, props?: StackProps) { - super(parent, name, props); - } - } - -.. _how_to_create_stack_in_region: - -How do I Create a Stack in a Specific Region? -============================================= - -To create a stack in **us-west-2** in your app, -and set the **region** property of the **env** -member of the **StackProps** argument to your stack when creating it, -as shown in the following example. - -.. code-block:: js - - new HelloStack(this, 'hello-cdk-us-west-2', { - env: { - region: 'us-west-2' - }}); - -.. _how_to_azs: - -How do I Get the Availability Zones in a Region? -================================================ - -You can get the AZs in the current region by using the -::py:class:`_aws-cdk_core.AvailabilityZoneProvider` class, -as shown in the following example. - -.. code-block:: js - - const zones: string[] = new AvailabilityZoneProvider(this).availabilityZones; - - for (let zone of zones) { - // do somethning for each zone! - } - -.. _how_to_: - -How do I ? -======================================== - - -.. _how_to_: - -How do I ? -======================================== - From f0e7bb5085967059e004120325138cd01bd4dea2 Mon Sep 17 00:00:00 2001 From: Breland Miley Date: Wed, 15 Aug 2018 00:15:21 -0700 Subject: [PATCH 20/33] Add a resolve to ArtifactPath (#571) * Add a resolve to ArtifactPath When using artifact path for template configuration, the CDK would get upset. This just adds a resolve that falls back to that location. Fixes #570 * Resolve location at l2 time --- packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts | 2 +- .../test/test.cloudformation-pipeline-actions.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts b/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts index 0538704f2e422..ccf019dfaba0e 100644 --- a/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts +++ b/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts @@ -188,7 +188,7 @@ export abstract class CloudFormationDeploymentAction extends CloudFormationActio Capabilities: (capabilities && capabilities.join(',')) || undefined, RoleArn: new cdk.Token(() => this.role.roleArn), ParameterOverrides: props.parameterOverrides, - TemplateConfiguration: props.templateConfiguration, + TemplateConfiguration: props.templateConfiguration ? props.templateConfiguration.location : undefined, StackName: props.stackName, }); diff --git a/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts b/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts index 3ce276d0f9542..31af69b00e18b 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts @@ -63,6 +63,7 @@ export = { changeSetName, role: changeSetExecRole, templatePath: new ArtifactPath(buildAction.artifact!, 'template.yaml'), + templateConfiguration: new ArtifactPath(buildAction.artifact!, 'templateConfig.json') }); new ExecuteChangeSet(stack, 'ExecuteChangeSetProd', { @@ -160,7 +161,8 @@ export = { ] }, "StackName": "BrelandsStack", - "TemplatePath": "OutputYo::template.yaml" + "TemplatePath": "OutputYo::template.yaml", + "TemplateConfiguration": "OutputYo::templateConfig.json" }, "InputArtifacts": [{"Name": "OutputYo"}], "Name": "BuildChangeSetProd", From 939015afd0bc4374afa7e4c59cfde7b91308d899 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 15 Aug 2018 13:25:34 +0200 Subject: [PATCH 21/33] feat(cdk): Tokens can be converted to strings (#518) Tokens (such as resource attributes) can now be implicitly converted into strings. This allows using any late-bound CloudFormation value where strings can be used in the host language, and the native string facilities of the host language can be used as well to further process strings. The process is reversed by the resolve() function during synthesis, which will split the string up again into string literals and CloudFormation intrinsics (combined by `{Fn::Join}`). Introduces `CloudFormationJSON.stringify` to produce a similar result, but used for JSON-encoding of complex objects that may contain Tokens. This is used in JSON-encoded policy statements, CloudFormation actions, state machine definitions, and more. Additional changes: - Add a VSCode launch config (with a helper script) to directly debug a unit test from within the IDE. This allows setting breakpoints. - SecretParameter no longer duckily implements Token; this will not work in other jsii languages anyway. - Nested `FnConcat` structures will be flattened in the CloudFormation output. Fixes #24 and #168. --- .../@aws-cdk/aws-cloudwatch/lib/dashboard.ts | 4 +- .../integ.alarm-and-dashboard.expected.json | 53 +++- .../aws-cloudwatch/test/test.dashboard.ts | 15 +- .../test/integ.project-bucket.expected.json | 56 ++-- .../test/integ.project-events.expected.json | 50 ++- .../aws-codebuild/test/test.codebuild.ts | 66 +--- ...g.pipeline-code-commit-build.expected.json | 38 +-- .../test/integ.pipeline-events.expected.json | 38 +-- .../aws-codepipeline/test/test.pipeline.ts | 2 +- .../@aws-cdk/aws-events/test/test.rule.ts | 11 +- .../aws-logs/lib/cross-account-destination.ts | 12 +- .../aws-logs/test/test.destination.ts | 3 +- .../lib/cloudformation/cloudformation-json.ts | 97 ++++++ .../cloudformation/cloudformation-token.ts | 28 ++ .../@aws-cdk/cdk/lib/cloudformation/fn.ts | 43 ++- .../@aws-cdk/cdk/lib/cloudformation/pseudo.ts | 6 +- .../cdk/lib/cloudformation/resource.ts | 3 +- .../@aws-cdk/cdk/lib/cloudformation/rule.ts | 3 +- .../@aws-cdk/cdk/lib/cloudformation/secret.ts | 9 +- .../@aws-cdk/cdk/lib/cloudformation/stack.ts | 5 +- .../lib/cloudformation/token-aware-jsonify.ts | 112 ------- packages/@aws-cdk/cdk/lib/core/tokens.ts | 294 +++++++++++++++++- packages/@aws-cdk/cdk/lib/core/util.ts | 2 +- packages/@aws-cdk/cdk/lib/index.ts | 3 +- packages/@aws-cdk/cdk/package-lock.json | 12 +- .../cdk/test/cloudformation/evaluate-cfn.ts | 62 ++++ .../test.cloudformation-json.ts | 175 +++++++++++ .../cdk/test/cloudformation/test.perms.ts | 2 +- .../cdk/test/cloudformation/test.secret.ts | 1 - .../@aws-cdk/cdk/test/core/test.tokens.ts | 139 ++++++++- .../cdk/test/test.token-aware-jsonify.ts | 74 ----- .../test/integ.rtv.lambda.expected.json | 35 +-- scripts/runtest.js | 16 + 33 files changed, 1005 insertions(+), 464 deletions(-) create mode 100644 packages/@aws-cdk/cdk/lib/cloudformation/cloudformation-json.ts create mode 100644 packages/@aws-cdk/cdk/lib/cloudformation/cloudformation-token.ts delete mode 100644 packages/@aws-cdk/cdk/lib/cloudformation/token-aware-jsonify.ts create mode 100644 packages/@aws-cdk/cdk/test/cloudformation/evaluate-cfn.ts create mode 100644 packages/@aws-cdk/cdk/test/cloudformation/test.cloudformation-json.ts delete mode 100644 packages/@aws-cdk/cdk/test/test.token-aware-jsonify.ts create mode 100755 scripts/runtest.js diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts b/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts index 77cac5955be08..5eebc6432e217 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts @@ -1,4 +1,4 @@ -import { Construct, Stack, Token, tokenAwareJsonify } from "@aws-cdk/cdk"; +import { CloudFormationJSON, Construct, Stack, Token } from "@aws-cdk/cdk"; import { cloudformation } from './cloudwatch.generated'; import { Column, Row } from "./layout"; import { IWidget } from "./widget"; @@ -33,7 +33,7 @@ export class Dashboard extends Construct { dashboardBody: new Token(() => { const column = new Column(...this.rows); column.position(0, 0); - return tokenAwareJsonify({ widgets: column.toJson() }); + return CloudFormationJSON.stringify({ widgets: column.toJson() }); }) }); } diff --git a/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.expected.json b/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.expected.json index 64feeab61fc77..bc6a19679b430 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.expected.json +++ b/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.expected.json @@ -7,6 +7,11 @@ "Type": "AWS::CloudWatch::Alarm", "Properties": { "ComparisonOperator": "GreaterThanOrEqualToThreshold", + "EvaluationPeriods": 3, + "MetricName": "ApproximateNumberOfMessagesVisible", + "Namespace": "AWS/SQS", + "Period": 300, + "Threshold": 100, "Dimensions": [ { "Name": "QueueName", @@ -18,41 +23,55 @@ } } ], - "EvaluationPeriods": 3, - "MetricName": "ApproximateNumberOfMessagesVisible", - "Namespace": "AWS/SQS", - "Period": 300, - "Statistic": "Average", - "Threshold": 100 + "Statistic": "Average" } }, "DashCCD7F836": { "Type": "AWS::CloudWatch::Dashboard", "Properties": { - "DashboardName": "aws-cdk-cloudwatch-DashCCD7F836", "DashboardBody": { - "Fn::Sub": [ - "{\"widgets\":[{\"type\":\"text\",\"width\":6,\"height\":2,\"x\":0,\"y\":0,\"properties\":{\"markdown\":\"# This is my dashboard\"}},{\"type\":\"text\",\"width\":6,\"height\":2,\"x\":6,\"y\":0,\"properties\":{\"markdown\":\"you like?\"}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":2,\"properties\":{\"view\":\"timeSeries\",\"title\":\"Messages in queue\",\"region\":\"${ref0}\",\"annotations\":{\"alarms\":[\"${ref1}\"]},\"yAxis\":{\"left\":{\"min\":0}}}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":8,\"properties\":{\"view\":\"timeSeries\",\"title\":\"More messages in queue with alarm annotation\",\"region\":\"${ref0}\",\"metrics\":[[\"AWS/SQS\",\"ApproximateNumberOfMessagesVisible\",\"QueueName\",\"${ref2}\",{\"yAxis\":\"left\",\"period\":300,\"stat\":\"Average\"}]],\"annotations\":{\"horizontal\":[{\"label\":\"ApproximateNumberOfMessagesVisible >= 100 for 3 datapoints within 15 minutes\",\"value\":100,\"yAxis\":\"left\"}]},\"yAxis\":{\"left\":{\"min\":0},\"right\":{\"min\":0}}}},{\"type\":\"metric\",\"width\":6,\"height\":3,\"x\":0,\"y\":14,\"properties\":{\"view\":\"singleValue\",\"title\":\"Current messages in queue\",\"region\":\"${ref0}\",\"metrics\":[[\"AWS/SQS\",\"ApproximateNumberOfMessagesVisible\",\"QueueName\",\"${ref2}\",{\"yAxis\":\"left\",\"period\":300,\"stat\":\"Average\"}]]}}]}", - { - "ref0": { + "Fn::Join": [ + "", + [ + "{\"widgets\":[{\"type\":\"text\",\"width\":6,\"height\":2,\"x\":0,\"y\":0,\"properties\":{\"markdown\":\"# This is my dashboard\"}},{\"type\":\"text\",\"width\":6,\"height\":2,\"x\":6,\"y\":0,\"properties\":{\"markdown\":\"you like?\"}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":2,\"properties\":{\"view\":\"timeSeries\",\"title\":\"Messages in queue\",\"region\":\"", + { "Ref": "AWS::Region" }, - "ref1": { + "\",\"annotations\":{\"alarms\":[\"", + { "Fn::GetAtt": [ "Alarm7103F465", "Arn" ] }, - "ref2": { + "\"]},\"yAxis\":{\"left\":{\"min\":0}}}},{\"type\":\"metric\",\"width\":6,\"height\":6,\"x\":0,\"y\":8,\"properties\":{\"view\":\"timeSeries\",\"title\":\"More messages in queue with alarm annotation\",\"region\":\"", + { + "Ref": "AWS::Region" + }, + "\",\"metrics\":[[\"AWS/SQS\",\"ApproximateNumberOfMessagesVisible\",\"QueueName\",\"", + { "Fn::GetAtt": [ "queue", "QueueName" ] - } - } + }, + "\",{\"yAxis\":\"left\",\"period\":300,\"stat\":\"Average\"}]],\"annotations\":{\"horizontal\":[{\"label\":\"ApproximateNumberOfMessagesVisible >= 100 for 3 datapoints within 15 minutes\",\"value\":100,\"yAxis\":\"left\"}]},\"yAxis\":{\"left\":{\"min\":0},\"right\":{\"min\":0}}}},{\"type\":\"metric\",\"width\":6,\"height\":3,\"x\":0,\"y\":14,\"properties\":{\"view\":\"singleValue\",\"title\":\"Current messages in queue\",\"region\":\"", + { + "Ref": "AWS::Region" + }, + "\",\"metrics\":[[\"AWS/SQS\",\"ApproximateNumberOfMessagesVisible\",\"QueueName\",\"", + { + "Fn::GetAtt": [ + "queue", + "QueueName" + ] + }, + "\",{\"yAxis\":\"left\",\"period\":300,\"stat\":\"Average\"}]]}}]}" + ] ] - } + }, + "DashboardName": "aws-cdk-cloudwatch-DashCCD7F836" } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudwatch/test/test.dashboard.ts b/packages/@aws-cdk/aws-cloudwatch/test/test.dashboard.ts index 30a21e379e714..8a62530bed35f 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/test.dashboard.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/test.dashboard.ts @@ -70,7 +70,7 @@ export = { test.done(); }, - 'tokens in widgets are retained through FnSub'(test: Test) { + 'tokens in widgets are retained'(test: Test) { // GIVEN const stack = new Stack(); const dashboard = new Dashboard(stack, 'Dash'); @@ -82,14 +82,11 @@ export = { // THEN expect(stack).to(haveResource('AWS::CloudWatch::Dashboard', { - DashboardBody: { "Fn::Sub": [ - // tslint:disable-next-line:max-line-length - "{\"widgets\":[{\"type\":\"metric\",\"width\":1,\"height\":1,\"x\":0,\"y\":0,\"properties\":{\"view\":\"timeSeries\",\"region\":\"${ref0}\",\"metrics\":[],\"annotations\":{\"horizontal\":[]},\"yAxis\":{\"left\":{\"min\":0},\"right\":{\"min\":0}}}}]}", - { - ref0: { Ref: "AWS::Region" } - } - ] - } + DashboardBody: { "Fn::Join": [ "", [ + "{\"widgets\":[{\"type\":\"metric\",\"width\":1,\"height\":1,\"x\":0,\"y\":0,\"properties\":{\"view\":\"timeSeries\",\"region\":\"", + { Ref: "AWS::Region" }, + "\",\"metrics\":[],\"annotations\":{\"horizontal\":[]},\"yAxis\":{\"left\":{\"min\":0},\"right\":{\"min\":0}}}}]}" + ]]} })); test.done(); diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-bucket.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.project-bucket.expected.json index b6dc07f204e9f..d2fdc48288a40 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.project-bucket.expected.json +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-bucket.expected.json @@ -86,16 +86,9 @@ ":", "log-group", ":", + "/aws/codebuild/", { - "Fn::Join": [ - "", - [ - "/aws/codebuild/", - { - "Ref": "MyProject39F7B0AE" - } - ] - ] + "Ref": "MyProject39F7B0AE" } ] ] @@ -126,16 +119,9 @@ ":", "log-group", ":", + "/aws/codebuild/", { - "Fn::Join": [ - "", - [ - "/aws/codebuild/", - { - "Ref": "MyProject39F7B0AE" - } - ] - ] + "Ref": "MyProject39F7B0AE" } ] ] @@ -160,8 +146,22 @@ "MyProject39F7B0AE": { "Type": "AWS::CodeBuild::Project", "Properties": { + "Artifacts": { + "Type": "NO_ARTIFACTS" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_LARGE", + "Image": "aws/codebuild/ubuntu-base:14.04", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "MyProjectRole9BBE5233", + "Arn" + ] + }, "Source": { - "Type": "S3", "Location": { "Fn::Join": [ "", @@ -173,22 +173,8 @@ "path/to/my/source.zip" ] ] - } - }, - "Artifacts": { - "Type": "NO_ARTIFACTS" - }, - "ServiceRole": { - "Fn::GetAtt": [ - "MyProjectRole9BBE5233", - "Arn" - ] - }, - "Environment": { - "Type": "LINUX_CONTAINER", - "PrivilegedMode": false, - "Image": "aws/codebuild/ubuntu-base:14.04", - "ComputeType": "BUILD_GENERAL1_LARGE" + }, + "Type": "S3" } } } diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-events.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.project-events.expected.json index d7f34cd26b3ae..2e08546fea552 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.project-events.expected.json +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-events.expected.json @@ -129,16 +129,9 @@ ":", "log-group", ":", + "/aws/codebuild/", { - "Fn::Join": [ - "", - [ - "/aws/codebuild/", - { - "Ref": "MyProject39F7B0AE" - } - ] - ] + "Ref": "MyProject39F7B0AE" } ] ] @@ -169,16 +162,9 @@ ":", "log-group", ":", + "/aws/codebuild/", { - "Fn::Join": [ - "", - [ - "/aws/codebuild/", - { - "Ref": "MyProject39F7B0AE" - } - ] - ] + "Ref": "MyProject39F7B0AE" } ] ] @@ -203,29 +189,29 @@ "MyProject39F7B0AE": { "Type": "AWS::CodeBuild::Project", "Properties": { - "Source": { - "Type": "CODECOMMIT", - "Location": { - "Fn::GetAtt": [ - "MyRepoF4F48043", - "CloneUrlHttp" - ] - } - }, "Artifacts": { "Type": "NO_ARTIFACTS" }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/ubuntu-base:14.04", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, "ServiceRole": { "Fn::GetAtt": [ "MyProjectRole9BBE5233", "Arn" ] }, - "Environment": { - "Type": "LINUX_CONTAINER", - "PrivilegedMode": false, - "Image": "aws/codebuild/ubuntu-base:14.04", - "ComputeType": "BUILD_GENERAL1_SMALL" + "Source": { + "Location": { + "Fn::GetAtt": [ + "MyRepoF4F48043", + "CloneUrlHttp" + ] + }, + "Type": "CODECOMMIT" } } }, diff --git a/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts b/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts index 6a719cb7b9d8f..c96c6f7c1ee9d 100644 --- a/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts +++ b/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts @@ -71,16 +71,9 @@ export = { ":", "log-group", ":", + "/aws/codebuild/", { - "Fn::Join": [ - "", - [ - "/aws/codebuild/", - { - "Ref": "MyProject39F7B0AE" - } - ] - ] + "Ref": "MyProject39F7B0AE" } ] ] @@ -111,16 +104,9 @@ export = { ":", "log-group", ":", + "/aws/codebuild/", { - "Fn::Join": [ - "", - [ - "/aws/codebuild/", - { - "Ref": "MyProject39F7B0AE" - } - ] - ] + "Ref": "MyProject39F7B0AE" } ] ] @@ -252,16 +238,9 @@ export = { ":", "log-group", ":", + "/aws/codebuild/", { - "Fn::Join": [ - "", - [ - "/aws/codebuild/", - { - "Ref": "MyProject39F7B0AE" - } - ] - ] + "Ref": "MyProject39F7B0AE" } ] ] @@ -292,16 +271,9 @@ export = { ":", "log-group", ":", + "/aws/codebuild/", { - "Fn::Join": [ - "", - [ - "/aws/codebuild/", - { - "Ref": "MyProject39F7B0AE" - } - ] - ] + "Ref": "MyProject39F7B0AE" } ] ] @@ -455,16 +427,9 @@ export = { ":", "log-group", ":", + "/aws/codebuild/", { - "Fn::Join": [ - "", - [ - "/aws/codebuild/", - { - "Ref": "MyProject39F7B0AE" - } - ] - ] + "Ref": "MyProject39F7B0AE" } ] ] @@ -495,16 +460,9 @@ export = { ":", "log-group", ":", + "/aws/codebuild/", { - "Fn::Join": [ - "", - [ - "/aws/codebuild/", - { - "Ref": "MyProject39F7B0AE" - } - ] - ] + "Ref": "MyProject39F7B0AE" } ] ] diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.expected.json index 86435a6edcfc2..c2d5d6a2ceb96 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.expected.json @@ -240,16 +240,9 @@ ":", "log-group", ":", + "/aws/codebuild/", { - "Fn::Join": [ - "", - [ - "/aws/codebuild/", - { - "Ref": "MyBuildProject30DB9D6E" - } - ] - ] + "Ref": "MyBuildProject30DB9D6E" } ] ] @@ -280,16 +273,9 @@ ":", "log-group", ":", + "/aws/codebuild/", { - "Fn::Join": [ - "", - [ - "/aws/codebuild/", - { - "Ref": "MyBuildProject30DB9D6E" - } - ] - ] + "Ref": "MyBuildProject30DB9D6E" } ] ] @@ -348,23 +334,23 @@ "MyBuildProject30DB9D6E": { "Type": "AWS::CodeBuild::Project", "Properties": { - "Source": { - "Type": "CODEPIPELINE" - }, "Artifacts": { "Type": "CODEPIPELINE" }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/ubuntu-base:14.04", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, "ServiceRole": { "Fn::GetAtt": [ "MyBuildProjectRole6B7E2258", "Arn" ] }, - "Environment": { - "Type": "LINUX_CONTAINER", - "PrivilegedMode": false, - "Image": "aws/codebuild/ubuntu-base:14.04", - "ComputeType": "BUILD_GENERAL1_SMALL" + "Source": { + "Type": "CODEPIPELINE" } } } diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.expected.json index bee622e2cab0a..13462c828f0a8 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.expected.json +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.expected.json @@ -413,16 +413,9 @@ ":", "log-group", ":", + "/aws/codebuild/", { - "Fn::Join": [ - "", - [ - "/aws/codebuild/", - { - "Ref": "BuildProject097C5DB7" - } - ] - ] + "Ref": "BuildProject097C5DB7" } ] ] @@ -453,16 +446,9 @@ ":", "log-group", ":", + "/aws/codebuild/", { - "Fn::Join": [ - "", - [ - "/aws/codebuild/", - { - "Ref": "BuildProject097C5DB7" - } - ] - ] + "Ref": "BuildProject097C5DB7" } ] ] @@ -521,23 +507,23 @@ "BuildProject097C5DB7": { "Type": "AWS::CodeBuild::Project", "Properties": { - "Source": { - "Type": "CODEPIPELINE" - }, "Artifacts": { "Type": "CODEPIPELINE" }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/ubuntu-base:14.04", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, "ServiceRole": { "Fn::GetAtt": [ "BuildProjectRoleAA92C755", "Arn" ] }, - "Environment": { - "Type": "LINUX_CONTAINER", - "PrivilegedMode": false, - "Image": "aws/codebuild/ubuntu-base:14.04", - "ComputeType": "BUILD_GENERAL1_SMALL" + "Source": { + "Type": "CODEPIPELINE" } } }, diff --git a/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts b/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts index 71606d3ca6df1..759a94ce50938 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts @@ -53,7 +53,7 @@ export = { stage: s1, artifactName: 'A', branch: 'branch', - oauthToken: secret, + oauthToken: secret.value, owner: 'foo', repo: 'bar' }); diff --git a/packages/@aws-cdk/aws-events/test/test.rule.ts b/packages/@aws-cdk/aws-events/test/test.rule.ts index ec5b7f0a8c51d..7ef0d9a0fd19d 100644 --- a/packages/@aws-cdk/aws-events/test/test.rule.ts +++ b/packages/@aws-cdk/aws-events/test/test.rule.ts @@ -262,15 +262,8 @@ export = { "", [ "\"", - { - "Fn::Join": [ - "", - [ - "a", - "b" - ] - ] - }, + "a", + "b", "\"" ] ] diff --git a/packages/@aws-cdk/aws-logs/lib/cross-account-destination.ts b/packages/@aws-cdk/aws-logs/lib/cross-account-destination.ts index 8f76d231cb798..0e982c9e61cc3 100644 --- a/packages/@aws-cdk/aws-logs/lib/cross-account-destination.ts +++ b/packages/@aws-cdk/aws-logs/lib/cross-account-destination.ts @@ -59,14 +59,13 @@ export class CrossAccountDestination extends cdk.Construct implements ILogSubscr constructor(parent: cdk.Construct, id: string, props: CrossAccountDestinationProps) { super(parent, id); - this.policyDocument = new cdk.PolicyDocument(); - // In the underlying model, the name is not optional, but we make it so anyway. const destinationName = props.destinationName || new cdk.Token(() => this.generateUniqueName()); this.resource = new cloudformation.DestinationResource(this, 'Resource', { destinationName, - destinationPolicy: new cdk.Token(() => !this.policyDocument.isEmpty ? JSON.stringify(this.policyDocument.resolve()) : ""), + // Must be stringified policy + destinationPolicy: new cdk.Token(() => this.stringifiedPolicyDocument()), roleArn: props.role.roleArn, targetArn: props.targetArn }); @@ -91,6 +90,13 @@ export class CrossAccountDestination extends cdk.Construct implements ILogSubscr const stack = cdk.Stack.find(this); return stack.name + '-' + this.resource.logicalId; } + + /** + * Return a stringified JSON version of the PolicyDocument + */ + private stringifiedPolicyDocument() { + return this.policyDocument.isEmpty ? '' : cdk.CloudFormationJSON.stringify(cdk.resolve(this.policyDocument)); + } } /** diff --git a/packages/@aws-cdk/aws-logs/test/test.destination.ts b/packages/@aws-cdk/aws-logs/test/test.destination.ts index ebd1a6e9ca515..63b33a0e62815 100644 --- a/packages/@aws-cdk/aws-logs/test/test.destination.ts +++ b/packages/@aws-cdk/aws-logs/test/test.destination.ts @@ -48,10 +48,9 @@ export = { // THEN expect(stack).to(haveResource('AWS::Logs::Destination', (props: any) => { - // tslint:disable-next-line:no-console const pol = JSON.parse(props.DestinationPolicy); - return pol.Statement[0].action[0] === 'logs:TalkToMe'; + return pol.Statement[0].Action === 'logs:TalkToMe'; })); test.done(); diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/cloudformation-json.ts b/packages/@aws-cdk/cdk/lib/cloudformation/cloudformation-json.ts new file mode 100644 index 0000000000000..b5d9b03d54e44 --- /dev/null +++ b/packages/@aws-cdk/cdk/lib/cloudformation/cloudformation-json.ts @@ -0,0 +1,97 @@ +import { resolve, Token } from "../core/tokens"; +import { CloudFormationToken, isIntrinsic } from "./cloudformation-token"; + +/** + * Class for JSON routines that are framework-aware + */ +export class CloudFormationJSON { + /** + * Turn an arbitrary structure potentially containing Tokens into a JSON string. + * + * Returns a Token which will evaluate to CloudFormation expression that + * will be evaluated by CloudFormation to the JSON representation of the + * input structure. + * + * All Tokens substituted in this way must return strings, or the evaluation + * in CloudFormation will fail. + */ + public static stringify(obj: any): Token { + return new Token(() => { + // Resolve inner value first so that if they evaluate to literals, we + // maintain the type (and discard 'undefined's). + // + // Then replace intrinsics with a special subclass of Token that + // overrides toJSON() to the marker string, so if we resolve() the + // strings again it evaluates to the right string. It also + // deep-escapes any strings inside the intrinsic, so that if literal + // strings are used in {Fn::Join} or something, they will end up + // escaped in the final JSON output. + const resolved = resolve(obj); + + // We can just directly return this value, since resolve() will be called + // on our return value anyway. + return JSON.stringify(deepReplaceIntrinsics(resolved)); + }); + + /** + * Recurse into a structure, replace all intrinsics with IntrinsicTokens. + */ + function deepReplaceIntrinsics(x: any): any { + if (isIntrinsic(x)) { + return wrapIntrinsic(x); + } + + if (Array.isArray(x)) { + return x.map(deepReplaceIntrinsics); + } + + if (typeof x === 'object') { + for (const key of Object.keys(x)) { + x[key] = deepReplaceIntrinsics(x[key]); + } + } + + return x; + } + + function wrapIntrinsic(intrinsic: any): IntrinsicToken { + return new IntrinsicToken(() => deepQuoteStringsForJSON(intrinsic)); + } + } +} + +/** + * Token that also stringifies in the toJSON() operation. + */ +class IntrinsicToken extends CloudFormationToken { + /** + * Special handler that gets called when JSON.stringify() is used. + */ + public toJSON() { + return this.toString(); + } +} + +/** + * Deep escape strings for use in a JSON context + */ +function deepQuoteStringsForJSON(x: any): any { + if (typeof x === 'string') { + // Whenever we escape a string we strip off the outermost quotes + // since we're already in a quoted context. + const stringified = JSON.stringify(x); + return stringified.substring(1, stringified.length - 1); + } + + if (Array.isArray(x)) { + return x.map(deepQuoteStringsForJSON); + } + + if (typeof x === 'object') { + for (const key of Object.keys(x)) { + x[key] = deepQuoteStringsForJSON(x[key]); + } + } + + return x; +} \ No newline at end of file diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/cloudformation-token.ts b/packages/@aws-cdk/cdk/lib/cloudformation/cloudformation-token.ts new file mode 100644 index 0000000000000..51479f86ff532 --- /dev/null +++ b/packages/@aws-cdk/cdk/lib/cloudformation/cloudformation-token.ts @@ -0,0 +1,28 @@ +import { resolve, Token } from "../core/tokens"; + +/** + * Base class for CloudFormation built-ins + */ +export class CloudFormationToken extends Token { + public concat(left: any | undefined, right: any | undefined): Token { + const parts = new Array(); + if (left !== undefined) { parts.push(left); } + parts.push(resolve(this)); + if (right !== undefined) { parts.push(right); } + return new FnConcat(...parts); + } +} + +import { FnConcat } from "./fn"; + +/** + * Return whether the given value represents a CloudFormation intrinsic + */ +export function isIntrinsic(x: any) { + if (Array.isArray(x) || x === null || typeof x !== 'object') { return false; } + + const keys = Object.keys(x); + if (keys.length !== 1) { return false; } + + return keys[0] === 'Ref' || keys[0].startsWith('Fn::'); +} \ No newline at end of file diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/fn.ts b/packages/@aws-cdk/cdk/lib/cloudformation/fn.ts index 661d1776aeebc..4d61e6fc169bf 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/fn.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/fn.ts @@ -1,11 +1,11 @@ -import { Token } from '../core/tokens'; +import { CloudFormationToken, isIntrinsic } from './cloudformation-token'; // tslint:disable:max-line-length /** * CloudFormation intrinsic functions. * http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html */ -export class Fn extends Token { +export class Fn extends CloudFormationToken { constructor(name: string, value: any) { super(() => ({ [name]: value })); } @@ -100,15 +100,54 @@ export class FnJoin extends Fn { * Alias for ``FnJoin('', listOfValues)``. */ export class FnConcat extends FnJoin { + private readonly listOfValues: any[]; + /** * Creates an ``Fn::Join`` function with an empty delimiter. * @param listOfValues The list of values to concatenate. */ constructor(...listOfValues: any[]) { + // Optimization: if any of the input arguments is also a FnConcat, + // splice their list of values into the current FnConcat. 'instanceof' + // can fail, but we do not depend depend on this for correctness. + // + // Do the same for resolved intrinsics, so we can detect this + // happening both at Token as well as at CloudFormation level. + + let i = 0; + while (i < listOfValues.length) { + const el = listOfValues[i]; + if (el instanceof FnConcat) { + listOfValues.splice(i, 1, ...el.listOfValues); + i += el.listOfValues.length; + } else if (isConcatIntrinsic(el)) { + const values = concatIntrinsicValues(el); + listOfValues.splice(i, 1, ...values); + i += values; + } else { + i++; + } + } + super('', listOfValues); + this.listOfValues = listOfValues; } } +/** + * Return whether the given object represents a CloudFormation intrinsic that is the result of a FnConcat resolution + */ +function isConcatIntrinsic(x: any) { + return isIntrinsic(x) && Object.keys(x)[0] === 'Fn::Join' && x['Fn::Join'][0] === ''; +} + +/** + * Return the concatted values of the concat intrinsic + */ +function concatIntrinsicValues(x: any) { + return x['Fn::Join'][1]; +} + /** * The intrinsic function ``Fn::Select`` returns a single object from a list of objects by index. */ diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/pseudo.ts b/packages/@aws-cdk/cdk/lib/cloudformation/pseudo.ts index 59b6edd4c90ee..1e39b291418e0 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/pseudo.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/pseudo.ts @@ -1,8 +1,8 @@ -import { Token } from '../core/tokens'; +import { CloudFormationToken } from './cloudformation-token'; -export class PseudoParameter extends Token { +export class PseudoParameter extends CloudFormationToken { constructor(name: string) { - super(() => ({ Ref: name })); + super({ Ref: name }, name); } } diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts b/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts index ae19bc1090e7d..ad831f7f27e20 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/resource.ts @@ -1,6 +1,7 @@ import { Construct } from '../core/construct'; import { Token } from '../core/tokens'; import { capitalizePropertyNames, ignoreEmpty } from '../core/util'; +import { CloudFormationToken } from './cloudformation-token'; import { Condition } from './condition'; import { CreationPolicy, DeletionPolicy, UpdatePolicy } from './resource-policy'; import { IDependable, Referenceable, StackElement } from './stack'; @@ -82,7 +83,7 @@ export class Resource extends Referenceable { * @param attributeName The name of the attribute. */ public getAtt(attributeName: string): Token { - return new Token(() => ({ 'Fn::GetAtt': [this.logicalId, attributeName] })); + return new CloudFormationToken({ 'Fn::GetAtt': [this.logicalId, attributeName] }, `${this.logicalId}.${attributeName}`); } /** diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/rule.ts b/packages/@aws-cdk/cdk/lib/cloudformation/rule.ts index ea58c6ad60cb1..a759f6a41659f 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/rule.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/rule.ts @@ -1,5 +1,4 @@ import { Construct } from '../core/construct'; -import { Token } from '../core/tokens'; import { capitalizePropertyNames } from '../core/util'; import { FnCondition } from './fn'; import { Referenceable } from './stack'; @@ -30,7 +29,7 @@ export interface RuleProps { * If the rule condition evaluates to false, the rule doesn't take effect. * If the function in the rule condition evaluates to true, expressions in each assert are evaluated and applied. */ - ruleCondition?: Token; + ruleCondition?: FnCondition; /** * Assertions which define the rule. diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/secret.ts b/packages/@aws-cdk/cdk/lib/cloudformation/secret.ts index 01c2e9b2e1b55..53f2f62ae545e 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/secret.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/secret.ts @@ -72,9 +72,9 @@ export interface SecretProps { */ export class SecretParameter extends Construct { /** - * A token for the secret value. + * The value of the secret parameter. */ - public value: Token; + public value: Secret; constructor(parent: Construct, name: string, props: SecretProps) { super(parent, name); @@ -93,9 +93,4 @@ export class SecretParameter extends Construct { this.value = param.ref; } - - // implicitly implements Token, and therefore Secret. - public resolve(): any { - return this.value; - } } diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts b/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts index 9f1b4a799a8fb..8a4211ad3cf0d 100644 --- a/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts +++ b/packages/@aws-cdk/cdk/lib/cloudformation/stack.ts @@ -3,6 +3,7 @@ import { App } from '../app'; import { Construct, PATH_SEP } from '../core/construct'; import { resolve, Token } from '../core/tokens'; import { Environment } from '../environment'; +import { CloudFormationToken } from './cloudformation-token'; import { HashedAddressingScheme, IAddressingScheme, LogicalIDs } from './logical-id'; import { Resource } from './resource'; @@ -398,8 +399,8 @@ export abstract class Referenceable extends StackElement { /** * Returns a token to a CloudFormation { Ref } that references this entity based on it's logical ID. */ - public get ref() { - return new Token(() => ({ Ref: this.logicalId })); + public get ref(): Token { + return new CloudFormationToken({ Ref: this.logicalId }, `${this.logicalId}.Ref`); } } diff --git a/packages/@aws-cdk/cdk/lib/cloudformation/token-aware-jsonify.ts b/packages/@aws-cdk/cdk/lib/cloudformation/token-aware-jsonify.ts deleted file mode 100644 index af36eaad9c4c9..0000000000000 --- a/packages/@aws-cdk/cdk/lib/cloudformation/token-aware-jsonify.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { istoken, resolve, Token } from '../core/tokens'; -import { FnSub } from './fn'; - -/** - * Jsonify a deep structure to a string while preserving tokens - * - * Sometimes we have JSON structures that contain CloudFormation intrinsics like - * { Ref } and { Fn::GetAtt }, but the model requires that we stringify the JSON - * structure and pass it into the parameter. - * - * Doing this makes it so that CloudFormation does not resolve the intrinsics - * anymore, since it does not look into every string. To resolve this, we - * stringify into a string and put placeholders in wich we substitute with the - * resolved references using { Fn::Sub }. - * - * Since the result is expected to be a stringified JSON, we need to make sure - * any textual values resolved from tokens are also stringified, so we also - * stringify any string values in resolved tokens (for example, "\n" will be - * replaced by "\\n", quotes will be escaped, etc). This might not be needed (or - * even could be harmful) for certain tokens (e.g. Fn::GetAtt), but we prefer to - * make the common case fool-proof, and hope for the best. - * - * Will only work correctly for intrinsics that return a string value. - */ -export function tokenAwareJsonify(structure: any): any { - // Our strategy is as follows: - // - // - Find all tokens, replace each of them with a string - // token that's highly unlikely to occur naturally. - // Attempt deduplication of the same intrinsic into the same - // string token. - // - JSONify the entire structure. - // - Replace things that LOOK like FnSub references - // with the escape string ${!NotSubstituted}. - // - Replace the special tokens with FnSub references, ${LikeThis}. - let counter = 0; - const tokenId: {[key: string]: string} = {}; - const substitutionMap: {[key: string]: any} = {}; - - function stringifyStrings(x: any): any { - if (typeof(x) === 'string') { - const jsonS = JSON.stringify(x); - return jsonS.substr(1, jsonS.length - 2); // trim quotes - } - - if (Array.isArray(x)) { - return x.map(stringifyStrings); - } - - if (typeof(x) === 'object') { - const result: any = {}; - for (const key of Object.keys(x)) { - result[key] = stringifyStrings(x[key]); - } - - return result; - } - - return x; - } - - function rememberToken(x: Token) { - // Get a representation of the resolved Token that we can use as a hash key. - const resolved = resolve(x); - const reprKey = JSON.stringify(resolved); - if (!(reprKey in tokenId)) { - tokenId[reprKey] = `ref${counter}`; - substitutionMap[tokenId[reprKey]] = stringifyStrings(resolved); - counter += 1; - } - return `<<>>`; - } - - function replaceTokens(x: any): any { - if (Array.isArray(x)) { - return x.map(replaceTokens); - } - - if (typeof x === 'object' && x !== null) { - if (istoken(x)) { - // This a token, remember and replace it. - return rememberToken(x); - } else { - // Recurse into regular object - for (const key of Object.keys(x)) { - x[key] = replaceTokens(x[key]); - } - return x; - } - } - - return x; - } - - structure = replaceTokens(structure); - - let stringified = JSON.stringify(structure); - - if (counter === 0) { - // No replacements - return stringified; - } - - // Escape things that shouldn't be substituted - // Translate ${Oops} -> ${!Oops} - stringified = stringified.replace(/\$\{([^}]+)\}/g, '$${!$1}'); - - // Now substitute our magic pattern with actual references - stringified = stringified.replace(/<<]+)>>>/g, '$${$1}'); - - return new FnSub(stringified, substitutionMap); -} diff --git a/packages/@aws-cdk/cdk/lib/core/tokens.ts b/packages/@aws-cdk/cdk/lib/core/tokens.ts index b0f953ee2544b..2aa6ed95538ff 100644 --- a/packages/@aws-cdk/cdk/lib/core/tokens.ts +++ b/packages/@aws-cdk/cdk/lib/core/tokens.ts @@ -7,16 +7,39 @@ import { Construct } from "./construct"; export const RESOLVE_METHOD = 'resolve'; /** - * Represents a lazy-evaluated value. Can be used to delay evaluation of a certain value - * in case, for example, that it requires some context or late-bound data. + * Represents a special or lazily-evaluated value. + * + * Can be used to delay evaluation of a certain value in case, for example, + * that it requires some context or late-bound data. Can also be used to + * mark values that need special processing at document rendering time. + * + * Tokens can be embedded into strings while retaining their original + * semantics. */ export class Token { + private tokenKey?: string; + /** - * Creates a token that resolves to `value`. If value is a function, - * the function is evaluated upon resolution and the value it returns will be - * uesd as the token's value. + * Creates a token that resolves to `value`. + * + * If value is a function, the function is evaluated upon resolution and + * the value it returns will be used as the token's value. + * + * displayName is used to represent the Token when it's embedded into a string; it + * will look something like this: + * + * "embedded in a larger string is ${Token[DISPLAY_NAME.123]}" + * + * This value is used as a hint to humans what the meaning of the Token is, + * and does not have any effect on the evaluation. + * + * Must contain only alphanumeric and simple separator characters (_.:-). + * + * @param valueOrFunction What this token will evaluate to, literal or function. + * @param displayName A human-readable display hint for this Token */ - constructor(private readonly valueOrFunction?: any) { } + constructor(private readonly valueOrFunction?: any, private readonly displayName?: string) { + } /** * @returns The resolved value for this token. @@ -29,13 +52,60 @@ export class Token { return value; } + + /** + * Return a reversible string representation of this token + * + * If the Token is initialized with a literal, the stringified value of the + * literal is returned. Otherwise, a special quoted string representation + * of the Token is returned that can be embedded into other strings. + * + * Strings with quoted Tokens in them can be restored back into + * complex values with the Tokens restored by calling `resolve()` + * on the string. + */ + public toString(): string { + const valueType = typeof this.valueOrFunction; + // Optimization: if we can immediately resolve this, don't bother + // registering a Token. + if (valueType === 'string' || valueType === 'number' || valueType === 'boolean') { + return this.valueOrFunction.toString(); + } + + if (this.tokenKey === undefined) { + this.tokenKey = TOKEN_STRING_MAP.register(this, this.displayName); + } + return this.tokenKey; + } + + /** + * Turn this Token into JSON + * + * This gets called by JSON.stringify(). We want to prohibit this, because + * it's not possible to do this properly, so we just throw an error here. + */ + public toJSON(): any { + // tslint:disable-next-line:max-line-length + throw new Error('JSON.stringify() cannot be applied to structure with a Token in it. Use a document-specific stringification method instead.'); + } + + /** + * Return a concated version of this Token in a string context + * + * The default implementation of this combines strings, but specialized + * implements of Token can return a more appropriate value. + */ + public concat(left: any | undefined, right: any | undefined): Token { + const parts = [left, resolve(this), right].filter(x => x !== undefined); + return new Token(parts.map(x => `${x}`).join('')); + } } /** * Returns true if obj is a token (i.e. has the resolve() method) * @param obj The object to test. */ -export function istoken(obj: any) { +export function isToken(obj: any): obj is Token { return typeof(obj[RESOLVE_METHOD]) === 'function'; } @@ -79,6 +149,13 @@ export function resolve(obj: any, prefix?: string[]): any { throw new Error(`Trying to resolve a non-data object. Only token are supported for lazy evaluation. Path: ${pathName}. Object: ${obj}`); } + // + // string - potentially replace all stringified Tokens + // + if (typeof(obj) === 'string') { + return TOKEN_STRING_MAP.resolveMarkers(obj as string); + } + // // primitives - as-is // @@ -91,7 +168,7 @@ export function resolve(obj: any, prefix?: string[]): any { // tokens - invoke 'resolve' and continue to resolve recursively // - if (istoken(obj)) { + if (isToken(obj)) { const value = obj[RESOLVE_METHOD](); return resolve(value, path); } @@ -133,3 +210,204 @@ export function resolve(obj: any, prefix?: string[]): any { return result; } + +/** + * Central place where we keep a mapping from Tokens to their String representation + * + * The string representation is used to embed token into strings, + * and stored to be able to + * + * All instances of TokenStringMap share the same storage, so that this process + * works even when different copies of the library are loaded. + */ +class TokenStringMap { + private readonly tokenMap: {[key: string]: Token}; + + constructor() { + const glob = global as any; + this.tokenMap = glob.__cdkTokenMap = glob.__cdkTokenMap || {}; + } + + /** + * Generating a unique string for this Token, returning a key + * + * Every call for the same Token will produce a new unique string, no + * attempt is made to deduplicate. Token objects should cache the + * value themselves, if required. + * + * The token can choose (part of) its own representation string with a + * hint. This may be used to produce aesthetically pleasing and + * recognizable token representations for humans. + */ + public register(token: Token, representationHint?: string): string { + const counter = Object.keys(this.tokenMap).length; + const representation = representationHint || `TOKEN`; + + const key = `${representation}.${counter}`; + if (new RegExp(`[^${VALID_KEY_CHARS}]`).exec(key)) { + throw new Error(`Invalid characters in token representation: ${key}`); + } + + this.tokenMap[key] = token; + return `${BEGIN_TOKEN_MARKER}${key}${END_TOKEN_MARKER}`; + } + + /** + * Replace any Token markers in this string with their resolved values + */ + public resolveMarkers(s: string): any { + const str = new TokenString(s, BEGIN_TOKEN_MARKER, `[${VALID_KEY_CHARS}]+`, END_TOKEN_MARKER); + const fragments = str.split(this.lookupToken.bind(this)); + return fragments.join(); + } + + /** + * Find a Token by key + */ + public lookupToken(key: string): Token { + if (!(key in this.tokenMap)) { + throw new Error(`Unrecognized token key: ${key}`); + } + + return this.tokenMap[key]; + } +} + +const BEGIN_TOKEN_MARKER = '${Token['; +const END_TOKEN_MARKER = ']}'; +const VALID_KEY_CHARS = 'a-zA-Z0-9:._-'; + +/** + * Singleton instance of the token string map + */ +const TOKEN_STRING_MAP = new TokenStringMap(); + +/** + * Interface that Token joiners implement + */ +export interface ITokenJoiner { + /** + * The name of the joiner. + * + * Must be unique per joiner: this value will be used to assert that there + * is exactly only type of joiner in a join operation. + */ + id: string; + + /** + * Return the language intrinsic that will combine the strings in the given engine + */ + join(fragments: any[]): any; +} + +/** + * A string with markers in it that can be resolved to external values + */ +class TokenString { + constructor( + private readonly str: string, + private readonly beginMarker: string, + private readonly idPattern: string, + private readonly endMarker: string) { + } + + /** + * Split string on markers, substituting markers with Tokens + */ + public split(lookup: (id: string) => Token): TokenStringFragments { + const re = new RegExp(`${regexQuote(this.beginMarker)}(${this.idPattern})${regexQuote(this.endMarker)}`, 'g'); + const ret = new TokenStringFragments(); + + let rest = 0; + let m = re.exec(this.str); + while (m) { + if (m.index > rest) { + ret.addString(this.str.substring(rest, m.index)); + } + + ret.addToken(lookup(m[1])); + + rest = re.lastIndex; + m = re.exec(this.str); + } + + if (rest < this.str.length) { + ret.addString(this.str.substring(rest)); + } + + return ret; + } +} + +/** + * Result of the split of a string with Tokens + * + * Either a literal part of the string, or an unresolved Token. + */ +type StringFragment = { type: 'string'; str: string }; +type TokenFragment = { type: 'token'; token: Token }; +type Fragment = StringFragment | TokenFragment; + +/** + * Fragments of a string with markers + */ +class TokenStringFragments { + private readonly fragments = new Array(); + + public values(): any[] { + return this.fragments.map(f => f.type === 'token' ? resolve(f.token) : f.str); + } + + public addString(str: string) { + this.fragments.push({ type: 'string', str }); + } + + public addToken(token: Token) { + this.fragments.push({ type: 'token', token }); + } + + /** + * Combine the resolved string fragments using the Tokens to join. + * + * Resolves the result. + */ + public join(): any { + if (this.fragments.length === 0) { return ''; } + if (this.fragments.length === 1) { return resolveFragment(this.fragments[0]); } + + const first = this.fragments[0]; + + let i; + let token: Token; + + if (first.type === 'token') { + token = first.token; + i = 1; + } else { + // We never have two strings in a row + token = (this.fragments[1] as TokenFragment).token.concat(first.str, undefined); + i = 2; + } + + while (i < this.fragments.length) { + token = token.concat(undefined, resolveFragment(this.fragments[i])); + i++; + } + + return resolve(token); + } +} + +/** + * Resolve the value from a single fragment + */ +function resolveFragment(fragment: Fragment): any { + return fragment.type === 'string' ? fragment.str : resolve(fragment.token); +} + +/** + * Quote a string for use in a regex + */ +function regexQuote(s: string) { + return s.replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&"); +} \ No newline at end of file diff --git a/packages/@aws-cdk/cdk/lib/core/util.ts b/packages/@aws-cdk/cdk/lib/core/util.ts index f1944f089e305..32e325c519f8a 100644 --- a/packages/@aws-cdk/cdk/lib/core/util.ts +++ b/packages/@aws-cdk/cdk/lib/core/util.ts @@ -47,4 +47,4 @@ export function ignoreEmpty(o: any): any { } return o; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/cdk/lib/index.ts b/packages/@aws-cdk/cdk/lib/index.ts index fa933eb4685ca..4a1167345295f 100644 --- a/packages/@aws-cdk/cdk/lib/index.ts +++ b/packages/@aws-cdk/cdk/lib/index.ts @@ -2,6 +2,8 @@ export * from './core/construct'; export * from './core/tokens'; export * from './core/jsx'; +export * from './cloudformation/cloudformation-json'; +export * from './cloudformation/cloudformation-token'; export * from './cloudformation/condition'; export * from './cloudformation/fn'; export * from './cloudformation/include'; @@ -19,7 +21,6 @@ export * from './cloudformation/tag'; export * from './cloudformation/removal-policy'; export * from './cloudformation/arn'; export * from './cloudformation/secret'; -export * from './cloudformation/token-aware-jsonify'; export * from './app'; export * from './context'; diff --git a/packages/@aws-cdk/cdk/package-lock.json b/packages/@aws-cdk/cdk/package-lock.json index 5d9a20916b9c9..59b67394eae31 100644 --- a/packages/@aws-cdk/cdk/package-lock.json +++ b/packages/@aws-cdk/cdk/package-lock.json @@ -12,7 +12,7 @@ "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-0.1.7.tgz", "integrity": "sha1-rcMgD6RxzCEbDaf1ZrcemLnWc0c=", "requires": { - "es5-ext": "0.8.x" + "es5-ext": "0.8.2" } }, "difflib": { @@ -20,7 +20,7 @@ "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", "integrity": "sha1-teMDYabbAjF21WKJLbhZQKcY9H4=", "requires": { - "heap": ">= 0.2.0" + "heap": "0.2.6" } }, "dreamopt": { @@ -28,7 +28,7 @@ "resolved": "https://registry.npmjs.org/dreamopt/-/dreamopt-0.6.0.tgz", "integrity": "sha1-2BPM2sjTnYrVJndVFKE92mZNa0s=", "requires": { - "wordwrap": ">=0.0.2" + "wordwrap": "1.0.0" } }, "es5-ext": { @@ -51,9 +51,9 @@ "resolved": "https://registry.npmjs.org/json-diff/-/json-diff-0.3.1.tgz", "integrity": "sha1-bbw64tJeB1p/1xvNmHRFhmb7aBs=", "requires": { - "cli-color": "~0.1.6", - "difflib": "~0.2.1", - "dreamopt": "~0.6.0" + "cli-color": "0.1.7", + "difflib": "0.2.4", + "dreamopt": "0.6.0" } }, "wordwrap": { diff --git a/packages/@aws-cdk/cdk/test/cloudformation/evaluate-cfn.ts b/packages/@aws-cdk/cdk/test/cloudformation/evaluate-cfn.ts new file mode 100644 index 0000000000000..4f8a419626ead --- /dev/null +++ b/packages/@aws-cdk/cdk/test/cloudformation/evaluate-cfn.ts @@ -0,0 +1,62 @@ +/** + * Simple function to evaluate CloudFormation intrinsics. + * + * Note that this function is not production quality, it exists to support tests. + */ +export function evaluateCFN(object: any, context: {[key: string]: string} = {}): any { + const intrinsics: any = { + 'Fn::Join'(separator: string, args: string[]) { + return args.map(evaluate).join(separator); + }, + + 'Ref'(logicalId: string) { + if (!(logicalId in context)) { + throw new Error(`Trying to evaluate Ref of '${logicalId}' but not in context!`); + } + return context[logicalId]; + }, + + 'Fn::GetAtt'(logicalId: string, attributeName: string) { + const key = `${logicalId}.${attributeName}`; + if (!(key in context)) { + throw new Error(`Trying to evaluate Fn::GetAtt of '${logicalId}.${attributeName}' but not in context!`); + } + return context[key]; + } + }; + + return evaluate(object); + + function evaluate(obj: any): any { + if (Array.isArray(obj)) { + return obj.map(evaluate); + } + + if (typeof obj === 'object') { + const keys = Object.keys(obj); + if (keys.length === 1 && (keys[0].startsWith('Fn::') || keys[0] === 'Ref')) { + return evaluateIntrinsic(keys[0], obj[keys[0]]); + } + + const ret: {[key: string]: any} = {}; + for (const key of keys) { + ret[key] = evaluateCFN(obj[key]); + } + return ret; + } + + return obj; + } + + function evaluateIntrinsic(name: string, args: any) { + if (!(name in intrinsics)) { + throw new Error(`Intrinsic ${name} not supported here`); + } + + if (!Array.isArray(args)) { + args = [args]; + } + + return intrinsics[name].apply(intrinsics, args); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/cdk/test/cloudformation/test.cloudformation-json.ts b/packages/@aws-cdk/cdk/test/cloudformation/test.cloudformation-json.ts new file mode 100644 index 0000000000000..389dee29cca89 --- /dev/null +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.cloudformation-json.ts @@ -0,0 +1,175 @@ +import { Test } from 'nodeunit'; +import { CloudFormationJSON, CloudFormationToken, FnConcat, resolve, Token } from '../../lib'; +import { evaluateCFN } from './evaluate-cfn'; + +export = { + 'plain JSON.stringify() on a Token fails'(test: Test) { + // GIVEN + const token = new Token(() => 'value'); + + // WHEN + test.throws(() => { + JSON.stringify({ token }); + }); + + test.done(); + }, + + 'string tokens can be JSONified and JSONification can be reversed'(test: Test) { + for (const token of tokensThatResolveTo('woof woof')) { + // GIVEN + const fido = { name: 'Fido', speaks: token }; + + // WHEN + const resolved = resolve(CloudFormationJSON.stringify(fido)); + + // THEN + test.deepEqual(evaluateCFN(resolved), '{"name":"Fido","speaks":"woof woof"}'); + } + + test.done(); + }, + + 'string tokens can be embedded while being JSONified'(test: Test) { + for (const token of tokensThatResolveTo('woof woof')) { + // GIVEN + const fido = { name: 'Fido', speaks: `deep ${token}` }; + + // WHEN + const resolved = resolve(CloudFormationJSON.stringify(fido)); + + // THEN + test.deepEqual(evaluateCFN(resolved), '{"name":"Fido","speaks":"deep woof woof"}'); + } + + test.done(); + }, + + 'integer Tokens behave correctly in stringification and JSONification'(test: Test) { + // GIVEN + const num = new Token(() => 1); + const embedded = `the number is ${num}`; + + // WHEN + test.equal(evaluateCFN(resolve(embedded)), "the number is 1"); + test.equal(evaluateCFN(resolve(CloudFormationJSON.stringify({ embedded }))), "{\"embedded\":\"the number is 1\"}"); + test.equal(evaluateCFN(resolve(CloudFormationJSON.stringify({ num }))), "{\"num\":1}"); + + test.done(); + }, + + 'tokens in strings survive additional TokenJSON.stringification()'(test: Test) { + // GIVEN + for (const token of tokensThatResolveTo('pong!')) { + // WHEN + const stringified = CloudFormationJSON.stringify(`ping? ${token}`); + + // THEN + test.equal(evaluateCFN(resolve(stringified)), '"ping? pong!"'); + } + + test.done(); + }, + + 'intrinsic Tokens embed correctly in JSONification'(test: Test) { + // GIVEN + const bucketName = new CloudFormationToken({ Ref: 'MyBucket' }); + + // WHEN + const resolved = resolve(CloudFormationJSON.stringify({ theBucket: bucketName })); + + // THEN + const context = {MyBucket: 'TheName'}; + test.equal(evaluateCFN(resolved, context), '{"theBucket":"TheName"}'); + + test.done(); + }, + + 'embedded string literals in intrinsics are escaped when calling TokenJSON.stringify()'(test: Test) { + // WHEN + const token = new FnConcat('Hello', 'This\nIs', 'Very "cool"'); + + // WHEN + const resolved = resolve(CloudFormationJSON.stringify({ + literal: 'I can also "contain" quotes', + token + })); + + // THEN + const expected = '{"literal":"I can also \\"contain\\" quotes","token":"HelloThis\\nIsVery \\"cool\\""}'; + test.equal(evaluateCFN(resolved), expected); + + test.done(); + }, + + 'Tokens in Tokens are handled correctly'(test: Test) { + // GIVEN + const bucketName = new CloudFormationToken({ Ref: 'MyBucket' }); + const combinedName = new FnConcat('The bucket name is ', bucketName); + + // WHEN + const resolved = resolve(CloudFormationJSON.stringify({ theBucket: combinedName })); + + // THEN + const context = {MyBucket: 'TheName'}; + test.equal(evaluateCFN(resolved, context), '{"theBucket":"The bucket name is TheName"}'); + + test.done(); + }, + + 'Doubly nested strings evaluate correctly in JSON context'(test: Test) { + // WHEN + const fidoSays = new Token(() => 'woof'); + + // WHEN + const resolved = resolve(CloudFormationJSON.stringify({ + information: `Did you know that Fido says: ${fidoSays}` + })); + + // THEN + test.deepEqual(evaluateCFN(resolved), '{"information":"Did you know that Fido says: woof"}'); + + test.done(); + }, + + 'Doubly nested intrinsics evaluate correctly in JSON context'(test: Test) { + // WHEN + const fidoSays = new CloudFormationToken(() => ({ Ref: 'Something' })); + + // WHEN + const resolved = resolve(CloudFormationJSON.stringify({ + information: `Did you know that Fido says: ${fidoSays}` + })); + + // THEN + const context = {Something: 'woof woof'}; + test.deepEqual(evaluateCFN(resolved, context), '{"information":"Did you know that Fido says: woof woof"}'); + + test.done(); + }, + + 'Quoted strings in embedded JSON context are escaped'(test: Test) { + // WHEN + const fidoSays = new Token(() => '"woof"'); + + // WHEN + const resolved = resolve(CloudFormationJSON.stringify({ + information: `Did you know that Fido says: ${fidoSays}` + })); + + // THEN + test.deepEqual(evaluateCFN(resolved), '{"information":"Did you know that Fido says: \\"woof\\""}'); + + test.done(); + }, +}; + +/** + * Return two Tokens, one of which evaluates to a Token directly, one which evaluates to it lazily + */ +function tokensThatResolveTo(value: any): Token[] { + return [ + new Token(value), + new Token(() => value) + ]; +} \ No newline at end of file diff --git a/packages/@aws-cdk/cdk/test/cloudformation/test.perms.ts b/packages/@aws-cdk/cdk/test/cloudformation/test.perms.ts index cb71587da67c2..b924f0e418d93 100644 --- a/packages/@aws-cdk/cdk/test/cloudformation/test.perms.ts +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.perms.ts @@ -26,7 +26,7 @@ export = { [ 'arn:', { Ref: 'AWS::Partition' }, ':iam::', - { 'Fn::Join': [ '', [ 'my', 'account', 'name' ] ] }, + 'my', 'account', 'name', ':root' ] ] } }, Condition: { StringEquals: { 'sts:ExternalId': '12221121221' } } }); diff --git a/packages/@aws-cdk/cdk/test/cloudformation/test.secret.ts b/packages/@aws-cdk/cdk/test/cloudformation/test.secret.ts index 1d58495ab9e61..a28a8ede46752 100644 --- a/packages/@aws-cdk/cdk/test/cloudformation/test.secret.ts +++ b/packages/@aws-cdk/cdk/test/cloudformation/test.secret.ts @@ -44,7 +44,6 @@ export = { // value resolves to a "Ref" test.deepEqual(resolve(mySecret.value), { Ref: 'MySecretParameterBB81DE58' }); - test.deepEqual(resolve(mySecret), { Ref: 'MySecretParameterBB81DE58' }); test.done(); } diff --git a/packages/@aws-cdk/cdk/test/core/test.tokens.ts b/packages/@aws-cdk/cdk/test/core/test.tokens.ts index 2330f1672410f..f62623f11bc2c 100644 --- a/packages/@aws-cdk/cdk/test/core/test.tokens.ts +++ b/packages/@aws-cdk/cdk/test/core/test.tokens.ts @@ -1,5 +1,6 @@ import { Test } from 'nodeunit'; -import { istoken, resolve, Token } from '../../lib'; +import { CloudFormationToken, isToken, resolve, Token } from '../../lib'; +import { evaluateCFN } from '../cloudformation/evaluate-cfn'; export = { 'resolve a plain old object should just return the object'(test: Test) { @@ -121,10 +122,10 @@ export = { test.done(); }, - 'istoken(obj) can be used to determine if an object is a token'(test: Test) { - test.ok(istoken({ resolve: () => 123 })); - test.ok(istoken({ a: 1, b: 2, resolve: () => 'hello' })); - test.ok(!istoken({ a: 1, b: 2, resolve: 3 })); + 'isToken(obj) can be used to determine if an object is a token'(test: Test) { + test.ok(isToken({ resolve: () => 123 })); + test.ok(isToken({ a: 1, b: 2, resolve: () => 'hello' })); + test.ok(!isToken({ a: 1, b: 2, resolve: 3 })); test.done(); }, @@ -141,7 +142,106 @@ export = { test.equal(date.toString(), resolved.toString()); test.done(); - } + }, + + 'tokens can be stringified and evaluated to conceptual value'(test: Test) { + // GIVEN + const token = new Token(() => 'woof woof'); + + // WHEN + const stringified = `The dog says: ${token}`; + const resolved = resolve(stringified); + + // THEN + test.deepEqual(evaluateCFN(resolved), 'The dog says: woof woof'); + test.done(); + }, + + 'Tokens stringification and reversing of CloudFormation Tokens is implemented using Fn::Join'(test: Test) { + // GIVEN + const token = new CloudFormationToken(() => 'woof woof'); + + // WHEN + const stringified = `The dog says: ${token}`; + const resolved = resolve(stringified); + + // THEN + test.deepEqual(resolved, { + 'Fn::Join': ['', ['The dog says: ', 'woof woof']] + }); + test.done(); + }, + + 'Doubly nested strings evaluate correctly in scalar context'(test: Test) { + // GIVEN + const token1 = new Token(() => "world"); + const token2 = new Token(() => `hello ${token1}`); + + // WHEN + const resolved1 = resolve(token2.toString()); + const resolved2 = resolve(token2); + + // THEN + test.deepEqual(evaluateCFN(resolved1), "hello world"); + test.deepEqual(evaluateCFN(resolved2), "hello world"); + + test.done(); + }, + + 'integer Tokens can be stringified and evaluate to conceptual value'(test: Test) { + // GIVEN + for (const token of literalTokensThatResolveTo(1)) { + // WHEN + const stringified = `the number is ${token}`; + const resolved = resolve(stringified); + + // THEN + test.deepEqual(evaluateCFN(resolved), 'the number is 1'); + } + test.done(); + }, + + 'intrinsic Tokens can be stringified and evaluate to conceptual value'(test: Test) { + // GIVEN + for (const bucketName of cloudFormationTokensThatResolveTo({ Ref: 'MyBucket' })) { + // WHEN + const resolved = resolve(`my bucket is named ${bucketName}`); + + // THEN + const context = {MyBucket: 'TheName'}; + test.equal(evaluateCFN(resolved, context), 'my bucket is named TheName'); + } + + test.done(); + }, + + 'tokens resolve properly in initial position'(test: Test) { + // GIVEN + for (const token of tokensThatResolveTo('Hello')) { + // WHEN + const resolved = resolve(`${token} world`); + + // THEN + test.equal(evaluateCFN(resolved), 'Hello world'); + } + + test.done(); + }, + + 'side-by-side Tokens resolve correctly'(test: Test) { + // GIVEN + for (const token1 of tokensThatResolveTo('Hello ')) { + for (const token2 of tokensThatResolveTo('world')) { + // WHEN + const resolved = resolve(`${token1}${token2}`); + + // THEN + test.equal(evaluateCFN(resolved), 'Hello world'); + } + } + + test.done(); + }, }; class Promise2 extends Token { @@ -176,3 +276,30 @@ class DataType extends BaseDataType { super(12); } } + +/** + * Return various flavors of Tokens that resolve to the given value + */ +function literalTokensThatResolveTo(value: any): Token[] { + return [ + new Token(value), + new Token(() => value) + ]; +} + +/** + * Return various flavors of Tokens that resolve to the given value + */ +function cloudFormationTokensThatResolveTo(value: any): Token[] { + return [ + new CloudFormationToken(value), + new CloudFormationToken(() => value) + ]; +} + +/** + * Return Tokens in both flavors that resolve to the given string + */ +function tokensThatResolveTo(value: string): Token[] { + return literalTokensThatResolveTo(value).concat(cloudFormationTokensThatResolveTo(value)); +} \ No newline at end of file diff --git a/packages/@aws-cdk/cdk/test/test.token-aware-jsonify.ts b/packages/@aws-cdk/cdk/test/test.token-aware-jsonify.ts deleted file mode 100644 index 1608c774bb8f1..0000000000000 --- a/packages/@aws-cdk/cdk/test/test.token-aware-jsonify.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Test } from 'nodeunit'; -import { AwsRegion, FnConcat, resolve, tokenAwareJsonify } from '../lib'; - -export = { - 'substitutes tokens'(test: Test) { - // WHEN - const result = tokenAwareJsonify({ - 'the region': new AwsRegion(), - 'the king': 'me', - }); - - // THEN - test.deepEqual(resolve(result), { - 'Fn::Sub': [ - '{"the region":"${ref0}","the king":"me"}', - { ref0: { Ref: 'AWS::Region' } } - ] - }); - - test.done(); - }, - - 'reuse token substitutions'(test: Test) { - // WHEN - const result = tokenAwareJsonify({ - 'the region': new AwsRegion(), - 'other region': new AwsRegion(), - 'the king': 'me', - }); - - // THEN - test.deepEqual(resolve(result), { - 'Fn::Sub': [ - '{"the region":"${ref0}","other region":"${ref0}","the king":"me"}', - { ref0: { Ref: 'AWS::Region' } } - ] - }); - - test.done(); - }, - - 'escape things that look like FnSub values'(test: Test) { - // WHEN - const result = tokenAwareJsonify({ - 'the region': new AwsRegion(), - 'the king': '${Me}', - }); - - // THEN - test.deepEqual(resolve(result), { - 'Fn::Sub': [ - '{"the region":"${ref0}","the king":"${!Me}"}', - { ref0: { Ref: 'AWS::Region' } } - ] - }); - - test.done(); - }, - - 'string values in resolved tokens should be represented as stringified strings'(test: Test) { - // WHEN - const result = tokenAwareJsonify({ - test1: new FnConcat('Hello', 'This\nIs', 'Very "cool"'), - }); - - // THEN - test.deepEqual(resolve(result), { 'Fn::Sub': - [ '{"test1":"${ref0}"}', - { ref0: - { 'Fn::Join': [ '', [ 'Hello', 'This\\nIs', 'Very \\"cool\\"' ] ] } } ] }); - - test.done(); - } -}; diff --git a/packages/@aws-cdk/runtime-values/test/integ.rtv.lambda.expected.json b/packages/@aws-cdk/runtime-values/test/integ.rtv.lambda.expected.json index a9b3a0ac18a24..6d0c2a5f1c8d1 100644 --- a/packages/@aws-cdk/runtime-values/test/integ.rtv.lambda.expected.json +++ b/packages/@aws-cdk/runtime-values/test/integ.rtv.lambda.expected.json @@ -78,21 +78,14 @@ ":", "parameter", "/", + "/rtv/", { - "Fn::Join": [ - "", - [ - "/rtv/", - { - "Ref": "AWS::StackName" - }, - "/", - "com.myorg", - "/", - "MyQueueURL" - ] - ] - } + "Ref": "AWS::StackName" + }, + "/", + "com.myorg", + "/", + "MyQueueURL" ] ] } @@ -114,13 +107,6 @@ "Code": { "ZipFile": "exports.handler = function runtimeCode(_event, _context, callback) {\n return callback();\n}" }, - "Environment": { - "Variables": { - "RTV_STACK_NAME": { - "Ref": "AWS::StackName" - } - } - }, "Handler": "index.handler", "Role": { "Fn::GetAtt": [ @@ -129,6 +115,13 @@ ] }, "Runtime": "nodejs6.10", + "Environment": { + "Variables": { + "RTV_STACK_NAME": { + "Ref": "AWS::StackName" + } + } + }, "Timeout": 30 }, "DependsOn": [ diff --git a/scripts/runtest.js b/scripts/runtest.js new file mode 100755 index 0000000000000..d501aca18f22c --- /dev/null +++ b/scripts/runtest.js @@ -0,0 +1,16 @@ +#!/usr/bin/env node +// Helper script to invoke 'nodeunit' on a .ts file. This should involve compilation, it doesn't right now. +const path = require('path'); + +// Unfortunately, nodeunit has no programmatic interface. Therefore, the +// easiest thing to do is rewrite the argv arguments where we change +// .ts into .js. + +for (let i = 2; i < process.argv.length; i++) { + if (process.argv[i].endsWith('.ts')) { + process.argv[i] = process.argv[i].substring(0, process.argv[i].length - 3) + '.js'; + } +} + +// Just pretend we're calling the program directly +require(path.resolve(__dirname, '..', 'node_modules', '.bin', 'nodeunit')); From 279df79f1be4660cd8e12aa6cdb2ddb465799a92 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 15 Aug 2018 13:42:51 +0200 Subject: [PATCH 22/33] fix(aws-cloudformation): Fix ParameterOverrides (#574) ParameterOverrides needs to be a JSON-encoded object, not an object itself. Fixes #566. --- .../lib/pipeline-actions.ts | 2 +- .../test.cloudformation-pipeline-actions.ts | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts b/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts index ccf019dfaba0e..7a7106063bdef 100644 --- a/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts +++ b/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts @@ -187,7 +187,7 @@ export abstract class CloudFormationDeploymentAction extends CloudFormationActio // This must be a string, so flatten the list to a comma-separated string. Capabilities: (capabilities && capabilities.join(',')) || undefined, RoleArn: new cdk.Token(() => this.role.roleArn), - ParameterOverrides: props.parameterOverrides, + ParameterOverrides: cdk.CloudFormationJSON.stringify(props.parameterOverrides), TemplateConfiguration: props.templateConfiguration ? props.templateConfiguration.location : undefined, StackName: props.stackName, }); diff --git a/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts b/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts index 31af69b00e18b..4a28eff000bc0 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts @@ -311,6 +311,45 @@ export = { test.done(); }, + + 'parameterOverrides are serialized as a string'(test: Test) { + // GIVEN + const stack = new TestFixture(); + + // WHEN + new CreateUpdateStack(stack, 'CreateUpdate', { + stage: stack.deployStage, + stackName: 'MyStack', + templatePath: stack.source.artifact.subartifact('template.yaml'), + parameterOverrides: { + RepoName: stack.repo.repositoryName + } + }); + + // THEN + expect(stack).to(haveResource('AWS::CodePipeline::Pipeline', { + "Stages": [ + { "Name": "Source" /* don't care about the rest */ }, + { + "Name": "Deploy", + "Actions": [ + { + "Configuration": { + "ParameterOverrides": { "Fn::Join": [ "", [ + "{\"RepoName\":\"", + { "Fn::GetAtt": [ "MyVeryImportantRepo11BC3EBD", "Name" ] }, + "\"}" + ]]} + }, + "Name": "CreateUpdate", + }, + ], + } + ] + })); + + test.done(); + } }; /** From 934ca5f36dce9c4b76513f2c32e8a940bdbd74c1 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 15 Aug 2018 15:32:37 +0200 Subject: [PATCH 23/33] v0.8.2 (#575) ### Features * __@aws-cdk/cdk__: Tokens can now be transparently embedded into strings and encoded into JSON without losing their semantics. This makes it possible to treat late-bound (deploy-time) values as if they were regular strings ([@rix0rrr] in [#518](https://github.com/awslabs/aws-cdk/pull/518)). * __@aws-cdk/aws-s3__: add support for bucket notifications to Lambda, SNS, and SQS targets ([@eladb] in [#201](https://github.com/awslabs/aws-cdk/pull/201), [#560](https://github.com/awslabs/aws-cdk/pull/560), [#561](https://github.com/awslabs/aws-cdk/pull/561), [#564](https://github.com/awslabs/aws-cdk/pull/564)) * __@aws-cdk/cdk__: non-alphanumeric characters can now be used as construct identifiers ([@eladb] in [#556](https://github.com/awslabs/aws-cdk/pull/556)) * __@aws-cdk/aws-iam__: add support for `maxSessionDuration` for Roles ([@eladb] in [#545](https://github.com/awslabs/aws-cdk/pull/545)). ### Changes * __@aws-cdk/aws-lambda__ (_**BREAKING**_): most classes renamed to be shorter and more in line with official service naming (`Lambda` renamed to `Function` or ommitted) ([@eladb] in [#550](https://github.com/awslabs/aws-cdk/pull/550)) * __@aws-cdk/aws-codepipeline__ (_**BREAKING**_): move all CodePipeline actions from `@aws-cdk/aws-xxx-codepipeline` packages into the regular `@aws-cdk/aws-xxx` service packages ([@skinny85] in [#459](https://github.com/awslabs/aws-cdk/pull/459)). * __@aws-cdk/aws-custom-resources__ (_**BREAKING**_): package was removed, and the Custom Resource construct added to the __@aws-cdk/aws-cloudformation__ package ([@rix0rrr] in [#513](https://github.com/awslabs/aws-cdk/pull/513)) ### Fixes * __@aws-cdk/aws-lambda__: Lambdas that are triggered by CloudWatch Events now show up in the console, and can only be triggered the indicated Event Rule. _**BREAKING**_ for middleware writers (as this introduces an API change), but transparent to regular consumers ([@eladb] in [#558](https://github.com/awslabs/aws-cdk/pull/558)) * __@aws-cdk/aws-codecommit__: fix a bug where `pollForSourceChanges` could not be set to `false` ([@maciejwalkowiak] in [#534](https://github.com/awslabs/aws-cdk/pull/534)) * __aws-cdk__: don't fail if the `~/.aws/credentials` file is missing ([@RomainMuller] in [#541](https://github.com/awslabs/aws-cdk/pull/541)) * __@aws-cdk/aws-cloudformation__: fix a bug in the CodePipeline actions to correctly support TemplateConfiguration ([@mindstorms6] in [#571](https://github.com/awslabs/aws-cdk/pull/571)). * __@aws-cdk/aws-cloudformation__: fix a bug in the CodePipeline actions to correctly support ParameterOverrides ([@mindstorms6] in [#574](https://github.com/awslabs/aws-cdk/pull/574)). ### Known Issues * `cdk init` will try to init a `git` repository and fail if no global `user.name` and `user.email` have been configured. --- CHANGELOG.md | 64 ++++++++++++++++--- examples/cdk-examples-java/package.json | 6 +- examples/cdk-examples-typescript/package.json | 34 +++++----- lerna.json | 2 +- packages/@aws-cdk/applet-js/package.json | 8 +-- packages/@aws-cdk/assert/package.json | 12 ++-- packages/@aws-cdk/assets/package.json | 20 +++--- packages/@aws-cdk/aws-apigateway/package.json | 12 ++-- .../aws-applicationautoscaling/package.json | 12 ++-- packages/@aws-cdk/aws-appsync/package.json | 12 ++-- packages/@aws-cdk/aws-athena/package.json | 12 ++-- .../@aws-cdk/aws-autoscaling/package.json | 12 ++-- .../aws-autoscalingplans/package.json | 12 ++-- packages/@aws-cdk/aws-batch/package.json | 12 ++-- packages/@aws-cdk/aws-budgets/package.json | 12 ++-- .../aws-certificatemanager/package.json | 14 ++-- packages/@aws-cdk/aws-cloud9/package.json | 12 ++-- .../@aws-cdk/aws-cloudformation/package.json | 22 +++---- packages/@aws-cdk/aws-cloudfront/package.json | 22 +++---- packages/@aws-cdk/aws-cloudtrail/package.json | 20 +++--- packages/@aws-cdk/aws-cloudwatch/package.json | 16 ++--- packages/@aws-cdk/aws-codebuild/package.json | 30 ++++----- packages/@aws-cdk/aws-codecommit/package.json | 22 +++---- packages/@aws-cdk/aws-codedeploy/package.json | 12 ++-- .../aws-codepipeline-api/package.json | 18 +++--- .../@aws-cdk/aws-codepipeline/package.json | 34 +++++----- packages/@aws-cdk/aws-cognito/package.json | 12 ++-- packages/@aws-cdk/aws-config/package.json | 12 ++-- .../@aws-cdk/aws-datapipeline/package.json | 12 ++-- packages/@aws-cdk/aws-dax/package.json | 12 ++-- .../aws-directoryservice/package.json | 12 ++-- packages/@aws-cdk/aws-dms/package.json | 12 ++-- packages/@aws-cdk/aws-dynamodb/package.json | 16 ++--- packages/@aws-cdk/aws-ec2/package.json | 24 +++---- packages/@aws-cdk/aws-ecr/package.json | 12 ++-- packages/@aws-cdk/aws-ecs/package.json | 12 ++-- packages/@aws-cdk/aws-efs/package.json | 12 ++-- packages/@aws-cdk/aws-eks/package.json | 12 ++-- .../@aws-cdk/aws-elasticache/package.json | 12 ++-- .../aws-elasticbeanstalk/package.json | 12 ++-- .../aws-elasticloadbalancing/package.json | 12 ++-- .../aws-elasticloadbalancingv2/package.json | 12 ++-- .../@aws-cdk/aws-elasticsearch/package.json | 12 ++-- packages/@aws-cdk/aws-emr/package.json | 12 ++-- packages/@aws-cdk/aws-events/package.json | 16 ++--- packages/@aws-cdk/aws-gamelift/package.json | 12 ++-- packages/@aws-cdk/aws-glue/package.json | 12 ++-- packages/@aws-cdk/aws-guardduty/package.json | 12 ++-- packages/@aws-cdk/aws-iam/package.json | 14 ++-- packages/@aws-cdk/aws-inspector/package.json | 12 ++-- packages/@aws-cdk/aws-iot/package.json | 12 ++-- packages/@aws-cdk/aws-kinesis/package.json | 18 +++--- .../aws-kinesisanalytics/package.json | 12 ++-- .../@aws-cdk/aws-kinesisfirehose/package.json | 12 ++-- packages/@aws-cdk/aws-kms/package.json | 16 ++--- packages/@aws-cdk/aws-lambda/package.json | 32 +++++----- packages/@aws-cdk/aws-logs/package.json | 16 ++--- packages/@aws-cdk/aws-neptune/package.json | 16 ++--- packages/@aws-cdk/aws-opsworks/package.json | 12 ++-- .../@aws-cdk/aws-quickstarts/package.json | 18 +++--- packages/@aws-cdk/aws-rds/package.json | 20 +++--- packages/@aws-cdk/aws-redshift/package.json | 12 ++-- packages/@aws-cdk/aws-route53/package.json | 18 +++--- .../aws-s3-notifications/package.json | 8 +-- packages/@aws-cdk/aws-s3/package.json | 20 +++--- packages/@aws-cdk/aws-sdb/package.json | 12 ++-- packages/@aws-cdk/aws-serverless/package.json | 12 ++-- .../@aws-cdk/aws-servicecatalog/package.json | 12 ++-- .../aws-servicediscovery/package.json | 12 ++-- packages/@aws-cdk/aws-ses/package.json | 12 ++-- packages/@aws-cdk/aws-sns/package.json | 28 ++++---- packages/@aws-cdk/aws-sqs/package.json | 20 +++--- packages/@aws-cdk/aws-ssm/package.json | 12 ++-- .../@aws-cdk/aws-stepfunctions/package.json | 12 ++-- packages/@aws-cdk/aws-waf/package.json | 12 ++-- .../@aws-cdk/aws-wafregional/package.json | 12 ++-- packages/@aws-cdk/aws-workspaces/package.json | 12 ++-- packages/@aws-cdk/cdk/package.json | 10 +-- packages/@aws-cdk/cfnspec/package.json | 6 +- .../@aws-cdk/cloudformation-diff/package.json | 8 +-- packages/@aws-cdk/cx-api/package.json | 6 +- packages/@aws-cdk/runtime-values/package.json | 22 +++---- packages/@aws-cdk/util/package.json | 6 +- packages/aws-cdk/package.json | 12 ++-- packages/simple-resource-bundler/package.json | 6 +- tools/cdk-build-tools/package.json | 6 +- tools/cdk-integ-tools/package.json | 12 ++-- tools/cfn2ts/package.json | 8 +-- tools/merkle-build/package.json | 4 +- tools/pkglint/package.json | 2 +- tools/pkgtools/package.json | 6 +- tools/y-npm/package.json | 6 +- 92 files changed, 674 insertions(+), 630 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65e29c9149920..f3072d1a3403b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,57 @@ -[@rix0rrr]: https://github.com/rix0rrr -[@sam-goodwin]: https://github.com/sam-goodwin -[@RomainMuller]: https://github.com/RomainMuller -[@eladb]: https://github.com/eladb -[@skinny85]: https://github.com/skinny85 -[@moofish32]: https://github.com/moofish32 -[@mpiroc]: https://github.com/mpiroc -[@Doug-AWS]: https://github.com/Doug-AWS -[@mindstorms6]: https://github.com/mindstorms6 -[@Mortifera]: https://github.com/Mortifera +[@rix0rrr]: https://github.com/rix0rrr +[@sam-goodwin]: https://github.com/sam-goodwin +[@RomainMuller]: https://github.com/RomainMuller +[@eladb]: https://github.com/eladb +[@skinny85]: https://github.com/skinny85 +[@moofish32]: https://github.com/moofish32 +[@mpiroc]: https://github.com/mpiroc +[@Doug-AWS]: https://github.com/Doug-AWS +[@mindstorms6]: https://github.com/mindstorms6 +[@Mortifera]: https://github.com/Mortifera +[@maciejwalkowiak]: https://github.com/maciejwalkowiak + +## 0.8.2 - 2018-08-15 + +### Features + +* __@aws-cdk/cdk__: Tokens can now be transparently embedded into strings and encoded into JSON without losing their + semantics. This makes it possible to treat late-bound (deploy-time) values as if they were regular strings ([@rix0rrr] + in [#518](https://github.com/awslabs/aws-cdk/pull/518)). +* __@aws-cdk/aws-s3__: add support for bucket notifications to Lambda, SNS, and SQS targets ([@eladb] in + [#201](https://github.com/awslabs/aws-cdk/pull/201), [#560](https://github.com/awslabs/aws-cdk/pull/560), + [#561](https://github.com/awslabs/aws-cdk/pull/561), [#564](https://github.com/awslabs/aws-cdk/pull/564)) +* __@aws-cdk/cdk__: non-alphanumeric characters can now be used as construct identifiers ([@eladb] in + [#556](https://github.com/awslabs/aws-cdk/pull/556)) +* __@aws-cdk/aws-iam__: add support for `maxSessionDuration` for Roles ([@eladb] in + [#545](https://github.com/awslabs/aws-cdk/pull/545)). + +### Changes + +* __@aws-cdk/aws-lambda__ (_**BREAKING**_): most classes renamed to be shorter and more in line with official service + naming (`Lambda` renamed to `Function` or ommitted) ([@eladb] in [#550](https://github.com/awslabs/aws-cdk/pull/550)) +* __@aws-cdk/aws-codepipeline__ (_**BREAKING**_): move all CodePipeline actions from `@aws-cdk/aws-xxx-codepipeline` packages + into the regular `@aws-cdk/aws-xxx` service packages ([@skinny85] in [#459](https://github.com/awslabs/aws-cdk/pull/459)). +* __@aws-cdk/aws-custom-resources__ (_**BREAKING**_): package was removed, and the Custom Resource construct added to + the __@aws-cdk/aws-cloudformation__ package ([@rix0rrr] in [#513](https://github.com/awslabs/aws-cdk/pull/513)) + +### Fixes + +* __@aws-cdk/aws-lambda__: Lambdas that are triggered by CloudWatch Events now show up in the console, and can only be + triggered the indicated Event Rule. _**BREAKING**_ for middleware writers (as this introduces an API change), but + transparent to regular consumers ([@eladb] in [#558](https://github.com/awslabs/aws-cdk/pull/558)) +* __@aws-cdk/aws-codecommit__: fix a bug where `pollForSourceChanges` could not be set to `false` ([@maciejwalkowiak] in + [#534](https://github.com/awslabs/aws-cdk/pull/534)) +* __aws-cdk__: don't fail if the `~/.aws/credentials` file is missing ([@RomainMuller] in + [#541](https://github.com/awslabs/aws-cdk/pull/541)) +* __@aws-cdk/aws-cloudformation__: fix a bug in the CodePipeline actions to correctly support TemplateConfiguration + ([@mindstorms6] in [#571](https://github.com/awslabs/aws-cdk/pull/571)). +* __@aws-cdk/aws-cloudformation__: fix a bug in the CodePipeline actions to correctly support ParameterOverrides + ([@mindstorms6] in [#574](https://github.com/awslabs/aws-cdk/pull/574)). + +### Known Issues + +* `cdk init` will try to init a `git` repository and fail if no global `user.name` and `user.email` have been + configured. ## 0.8.1 - 2018-08-08 diff --git a/examples/cdk-examples-java/package.json b/examples/cdk-examples-java/package.json index 81ea917533ff9..e4eaddd112da1 100644 --- a/examples/cdk-examples-java/package.json +++ b/examples/cdk-examples-java/package.json @@ -1,6 +1,6 @@ { "name": "cdk-examples-java", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK examples in Java", "private": true, "repository": { @@ -22,7 +22,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "aws-cdk": "^0.8.1", - "pkgtools": "^0.8.1" + "aws-cdk": "^0.8.2", + "pkgtools": "^0.8.2" } } diff --git a/examples/cdk-examples-typescript/package.json b/examples/cdk-examples-typescript/package.json index bd8b67fd02de5..25b7b6c879a17 100644 --- a/examples/cdk-examples-typescript/package.json +++ b/examples/cdk-examples-typescript/package.json @@ -1,6 +1,6 @@ { "name": "cdk-examples-typescript", - "version": "0.8.1", + "version": "0.8.2", "description": "A bunch of CDK examples", "private": true, "scripts": { @@ -18,24 +18,24 @@ }, "license": "Apache-2.0", "devDependencies": { - "aws-cdk": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "pkglint": "^0.8.1" + "aws-cdk": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-cloudformation": "^0.8.1", - "@aws-cdk/aws-cognito": "^0.8.1", - "@aws-cdk/aws-dynamodb": "^0.8.1", - "@aws-cdk/aws-ec2": "^0.8.1", - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-lambda": "^0.8.1", - "@aws-cdk/aws-neptune": "^0.8.1", - "@aws-cdk/aws-rds": "^0.8.1", - "@aws-cdk/aws-s3": "^0.8.1", - "@aws-cdk/aws-sns": "^0.8.1", - "@aws-cdk/aws-sqs": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1", - "@aws-cdk/runtime-values": "^0.8.1" + "@aws-cdk/aws-cloudformation": "^0.8.2", + "@aws-cdk/aws-cognito": "^0.8.2", + "@aws-cdk/aws-dynamodb": "^0.8.2", + "@aws-cdk/aws-ec2": "^0.8.2", + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-lambda": "^0.8.2", + "@aws-cdk/aws-neptune": "^0.8.2", + "@aws-cdk/aws-rds": "^0.8.2", + "@aws-cdk/aws-s3": "^0.8.2", + "@aws-cdk/aws-sns": "^0.8.2", + "@aws-cdk/aws-sqs": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2", + "@aws-cdk/runtime-values": "^0.8.2" }, "repository": { "url": "https://github.com/awslabs/aws-cdk.git", diff --git a/lerna.json b/lerna.json index 8df563ef76ced..43e43b98a5d1b 100644 --- a/lerna.json +++ b/lerna.json @@ -7,5 +7,5 @@ "examples/*" ], "rejectCycles": "true", - "version": "0.8.1" + "version": "0.8.2" } diff --git a/packages/@aws-cdk/applet-js/package.json b/packages/@aws-cdk/applet-js/package.json index 0afae78527932..90935faef04ed 100644 --- a/packages/@aws-cdk/applet-js/package.json +++ b/packages/@aws-cdk/applet-js/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/applet-js", - "version": "0.8.1", + "version": "0.8.2", "description": "Javascript CDK applet host program", "main": "bin/cdk-applet-js.js", "types": "bin/cdk-applet-js.d.ts", @@ -23,11 +23,11 @@ "license": "Apache-2.0", "devDependencies": { "@types/yamljs": "^0.2.0", - "cdk-build-tools": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1", + "@aws-cdk/cdk": "^0.8.2", "source-map-support": "^0.5.6", "yamljs": "^0.2.0" }, diff --git a/packages/@aws-cdk/assert/package.json b/packages/@aws-cdk/assert/package.json index a215bdaf7ad65..8250d628966dd 100644 --- a/packages/@aws-cdk/assert/package.json +++ b/packages/@aws-cdk/assert/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/assert", - "version": "0.8.1", + "version": "0.8.2", "description": "An assertion library for use with CDK Apps", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -23,13 +23,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "cdk-build-tools": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1", - "@aws-cdk/cloudformation-diff": "^0.8.1", - "@aws-cdk/cx-api": "^0.8.1", + "@aws-cdk/cdk": "^0.8.2", + "@aws-cdk/cloudformation-diff": "^0.8.2", + "@aws-cdk/cx-api": "^0.8.2", "source-map-support": "^0.5.6" }, "repository": { diff --git a/packages/@aws-cdk/assets/package.json b/packages/@aws-cdk/assets/package.json index 3b55648e2bb1f..c49caff605aff 100644 --- a/packages/@aws-cdk/assets/package.json +++ b/packages/@aws-cdk/assets/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/assets", - "version": "0.8.1", + "version": "0.8.2", "description": "Integration of CDK apps with local assets", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -43,17 +43,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "aws-cdk": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "aws-cdk": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-s3": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1", - "@aws-cdk/cx-api": "^0.8.1" + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-s3": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2", + "@aws-cdk/cx-api": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-apigateway/package.json b/packages/@aws-cdk/aws-apigateway/package.json index b87af76b40d9c..5670f75631835 100644 --- a/packages/@aws-cdk/aws-apigateway/package.json +++ b/packages/@aws-cdk/aws-apigateway/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-apigateway", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::ApiGateway", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-applicationautoscaling/package.json b/packages/@aws-cdk/aws-applicationautoscaling/package.json index 18dd0d5e64c09..2a17281776d42 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/package.json +++ b/packages/@aws-cdk/aws-applicationautoscaling/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-applicationautoscaling", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::ApplicationAutoScaling", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-appsync/package.json b/packages/@aws-cdk/aws-appsync/package.json index c13bd6a79b947..315794105d2f2 100644 --- a/packages/@aws-cdk/aws-appsync/package.json +++ b/packages/@aws-cdk/aws-appsync/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-appsync", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::AppSync", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-athena/package.json b/packages/@aws-cdk/aws-athena/package.json index 60b73cd603af0..c4787a4e1cb7c 100644 --- a/packages/@aws-cdk/aws-athena/package.json +++ b/packages/@aws-cdk/aws-athena/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-athena", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::Athena", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-autoscaling/package.json b/packages/@aws-cdk/aws-autoscaling/package.json index 1c92c71252ebe..ee345dd26cf23 100644 --- a/packages/@aws-cdk/aws-autoscaling/package.json +++ b/packages/@aws-cdk/aws-autoscaling/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-autoscaling", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::AutoScaling", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-autoscalingplans/package.json b/packages/@aws-cdk/aws-autoscalingplans/package.json index a6f730c2578ac..5c74515f3adc4 100644 --- a/packages/@aws-cdk/aws-autoscalingplans/package.json +++ b/packages/@aws-cdk/aws-autoscalingplans/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-autoscalingplans", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::AutoScalingPlans", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-batch/package.json b/packages/@aws-cdk/aws-batch/package.json index 51d669707d89d..ece92006d3c8e 100644 --- a/packages/@aws-cdk/aws-batch/package.json +++ b/packages/@aws-cdk/aws-batch/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-batch", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::Batch", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-budgets/package.json b/packages/@aws-cdk/aws-budgets/package.json index e989a87fbe3a8..a5324bc311d76 100644 --- a/packages/@aws-cdk/aws-budgets/package.json +++ b/packages/@aws-cdk/aws-budgets/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-budgets", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::Budgets", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-certificatemanager/package.json b/packages/@aws-cdk/aws-certificatemanager/package.json index 30a0598ab1c1d..dc480300312f2 100644 --- a/packages/@aws-cdk/aws-certificatemanager/package.json +++ b/packages/@aws-cdk/aws-certificatemanager/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-certificatemanager", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::CertificateManager", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,14 +46,14 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-cloud9/package.json b/packages/@aws-cdk/aws-cloud9/package.json index 8dfc8b72cb3d8..d6a6205aa0116 100644 --- a/packages/@aws-cdk/aws-cloud9/package.json +++ b/packages/@aws-cdk/aws-cloud9/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-cloud9", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::Cloud9", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-cloudformation/package.json b/packages/@aws-cdk/aws-cloudformation/package.json index a3bbcb4e6c495..d83c026aaec66 100644 --- a/packages/@aws-cdk/aws-cloudformation/package.json +++ b/packages/@aws-cdk/aws-cloudformation/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-cloudformation", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS CloudFormation", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -50,18 +50,18 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1", - "@aws-cdk/aws-codepipeline-api": "^0.8.1", - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-lambda": "^0.8.1", - "@aws-cdk/aws-sns": "^0.8.1" + "@aws-cdk/aws-codepipeline-api": "^0.8.2", + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-lambda": "^0.8.2", + "@aws-cdk/aws-sns": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-cloudfront/package.json b/packages/@aws-cdk/aws-cloudfront/package.json index 47a3e6f8c538a..755f05b2cab47 100644 --- a/packages/@aws-cdk/aws-cloudfront/package.json +++ b/packages/@aws-cdk/aws-cloudfront/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-cloudfront", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS CloudFront", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,19 +46,19 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", + "@aws-cdk/assert": "^0.8.2", "aws-sdk": "^2.259.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-certificatemanager": "^0.8.1", - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-kms": "^0.8.1", - "@aws-cdk/aws-s3": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-certificatemanager": "^0.8.2", + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-kms": "^0.8.2", + "@aws-cdk/aws-s3": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-cloudtrail/package.json b/packages/@aws-cdk/aws-cloudtrail/package.json index 24bb505f1283e..1a7d4b77acf9e 100644 --- a/packages/@aws-cdk/aws-cloudtrail/package.json +++ b/packages/@aws-cdk/aws-cloudtrail/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-cloudtrail", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS CloudTrail", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -45,19 +45,19 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", + "@aws-cdk/assert": "^0.8.2", "aws-sdk": "^2.259.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", "colors": "^1.2.1", - "pkglint": "^0.8.1" + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-kms": "^0.8.1", - "@aws-cdk/aws-logs": "^0.8.1", - "@aws-cdk/aws-s3": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-kms": "^0.8.2", + "@aws-cdk/aws-logs": "^0.8.2", + "@aws-cdk/aws-s3": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-cloudwatch/package.json b/packages/@aws-cdk/aws-cloudwatch/package.json index 7f0a916852175..08552bd88fa63 100644 --- a/packages/@aws-cdk/aws-cloudwatch/package.json +++ b/packages/@aws-cdk/aws-cloudwatch/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-cloudwatch", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS CloudWatch", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,15 +46,15 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index c38854738f6aa..5d2f4e13f00d7 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-codebuild", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS CodeBuild", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -49,23 +49,23 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "@aws-cdk/aws-sns": "^0.8.1", + "@aws-cdk/assert": "^0.8.2", + "@aws-cdk/aws-sns": "^0.8.2", "aws-sdk": "^2.259.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-cloudwatch": "^0.8.1", - "@aws-cdk/aws-codecommit": "^0.8.1", - "@aws-cdk/aws-codepipeline-api": "^0.8.1", - "@aws-cdk/aws-events": "^0.8.1", - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-kms": "^0.8.1", - "@aws-cdk/aws-s3": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-cloudwatch": "^0.8.2", + "@aws-cdk/aws-codecommit": "^0.8.2", + "@aws-cdk/aws-codepipeline-api": "^0.8.2", + "@aws-cdk/aws-events": "^0.8.2", + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-kms": "^0.8.2", + "@aws-cdk/aws-s3": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-codecommit/package.json b/packages/@aws-cdk/aws-codecommit/package.json index 42c7767b8f11e..1a94afbe6649a 100644 --- a/packages/@aws-cdk/aws-codecommit/package.json +++ b/packages/@aws-cdk/aws-codecommit/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-codecommit", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS CodeCommit", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -50,19 +50,19 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "@aws-cdk/aws-sns": "^0.8.1", + "@aws-cdk/assert": "^0.8.2", + "@aws-cdk/aws-sns": "^0.8.2", "aws-sdk": "^2.259.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-codepipeline-api": "^0.8.1", - "@aws-cdk/aws-events": "^0.8.1", - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-codepipeline-api": "^0.8.2", + "@aws-cdk/aws-events": "^0.8.2", + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-codedeploy/package.json b/packages/@aws-cdk/aws-codedeploy/package.json index 9d29c37b3f9fa..93b3ad7ca5c5a 100644 --- a/packages/@aws-cdk/aws-codedeploy/package.json +++ b/packages/@aws-cdk/aws-codedeploy/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-codedeploy", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::CodeDeploy", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-codepipeline-api/package.json b/packages/@aws-cdk/aws-codepipeline-api/package.json index 72ce83307739f..e67fa2dd3d73b 100644 --- a/packages/@aws-cdk/aws-codepipeline-api/package.json +++ b/packages/@aws-cdk/aws-codepipeline-api/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-codepipeline-api", - "version": "0.8.1", + "version": "0.8.2", "description": "Actions API for AWS Code Pipeline", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,16 +46,16 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-events": "^0.8.1", - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1", - "@aws-cdk/util": "^0.8.1" + "@aws-cdk/aws-events": "^0.8.2", + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2", + "@aws-cdk/util": "^0.8.2" }, "bundledDependencies": [ "@aws-cdk/util" diff --git a/packages/@aws-cdk/aws-codepipeline/package.json b/packages/@aws-cdk/aws-codepipeline/package.json index ebc15faf11afc..9e5c27db99624 100644 --- a/packages/@aws-cdk/aws-codepipeline/package.json +++ b/packages/@aws-cdk/aws-codepipeline/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-codepipeline", - "version": "0.8.1", + "version": "0.8.2", "description": "Better interface to AWS Code Pipeline", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -52,24 +52,24 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "@aws-cdk/aws-cloudformation": "^0.8.1", - "@aws-cdk/aws-codebuild": "^0.8.1", - "@aws-cdk/aws-codecommit": "^0.8.1", - "@aws-cdk/aws-lambda": "^0.8.1", - "@aws-cdk/aws-sns": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "@aws-cdk/aws-cloudformation": "^0.8.2", + "@aws-cdk/aws-codebuild": "^0.8.2", + "@aws-cdk/aws-codecommit": "^0.8.2", + "@aws-cdk/aws-lambda": "^0.8.2", + "@aws-cdk/aws-sns": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-codepipeline-api": "^0.8.1", - "@aws-cdk/aws-events": "^0.8.1", - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-s3": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1", - "@aws-cdk/util": "^0.8.1" + "@aws-cdk/aws-codepipeline-api": "^0.8.2", + "@aws-cdk/aws-events": "^0.8.2", + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-s3": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2", + "@aws-cdk/util": "^0.8.2" }, "bundledDependencies": [ "@aws-cdk/util" diff --git a/packages/@aws-cdk/aws-cognito/package.json b/packages/@aws-cdk/aws-cognito/package.json index f5b7e96963467..d8a0c4fda9344 100644 --- a/packages/@aws-cdk/aws-cognito/package.json +++ b/packages/@aws-cdk/aws-cognito/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-cognito", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::Cognito", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-config/package.json b/packages/@aws-cdk/aws-config/package.json index 176c075248c6c..e8cc8dfdbc80e 100644 --- a/packages/@aws-cdk/aws-config/package.json +++ b/packages/@aws-cdk/aws-config/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-config", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::Config", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-datapipeline/package.json b/packages/@aws-cdk/aws-datapipeline/package.json index 1e401a87e94c0..4ac01bd20fb22 100644 --- a/packages/@aws-cdk/aws-datapipeline/package.json +++ b/packages/@aws-cdk/aws-datapipeline/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-datapipeline", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::DataPipeline", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-dax/package.json b/packages/@aws-cdk/aws-dax/package.json index d7f9491f18ce3..07250c7d88d46 100644 --- a/packages/@aws-cdk/aws-dax/package.json +++ b/packages/@aws-cdk/aws-dax/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-dax", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::DAX", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-directoryservice/package.json b/packages/@aws-cdk/aws-directoryservice/package.json index 3830486b5937c..fe881730225de 100644 --- a/packages/@aws-cdk/aws-directoryservice/package.json +++ b/packages/@aws-cdk/aws-directoryservice/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-directoryservice", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::DirectoryService", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-dms/package.json b/packages/@aws-cdk/aws-dms/package.json index b1c3198753d3b..e077e71332233 100644 --- a/packages/@aws-cdk/aws-dms/package.json +++ b/packages/@aws-cdk/aws-dms/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-dms", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::DMS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json index 909dea95967f7..cfb4e958278a8 100644 --- a/packages/@aws-cdk/aws-dynamodb/package.json +++ b/packages/@aws-cdk/aws-dynamodb/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-dynamodb", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS DynamoDB", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,15 +46,15 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-ec2/package.json b/packages/@aws-cdk/aws-ec2/package.json index ded1a6d16d3e1..f1d3ec2c0d6ce 100644 --- a/packages/@aws-cdk/aws-ec2/package.json +++ b/packages/@aws-cdk/aws-ec2/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-ec2", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS EC2", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,19 +46,19 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-autoscaling": "^0.8.1", - "@aws-cdk/aws-elasticloadbalancing": "^0.8.1", - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-sns": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1", - "@aws-cdk/util": "^0.8.1" + "@aws-cdk/aws-autoscaling": "^0.8.2", + "@aws-cdk/aws-elasticloadbalancing": "^0.8.2", + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-sns": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2", + "@aws-cdk/util": "^0.8.2" }, "bundledDependencies": [ "@aws-cdk/util" diff --git a/packages/@aws-cdk/aws-ecr/package.json b/packages/@aws-cdk/aws-ecr/package.json index f90f4163a2d09..be91cef2b48d2 100644 --- a/packages/@aws-cdk/aws-ecr/package.json +++ b/packages/@aws-cdk/aws-ecr/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-ecr", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::ECR", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-ecs/package.json b/packages/@aws-cdk/aws-ecs/package.json index db3ed893d296f..861cf4d400b82 100644 --- a/packages/@aws-cdk/aws-ecs/package.json +++ b/packages/@aws-cdk/aws-ecs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-ecs", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::ECS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-efs/package.json b/packages/@aws-cdk/aws-efs/package.json index 03f4f3544bbea..82acfda9bbce8 100644 --- a/packages/@aws-cdk/aws-efs/package.json +++ b/packages/@aws-cdk/aws-efs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-efs", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::EFS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-eks/package.json b/packages/@aws-cdk/aws-eks/package.json index a8d792a08d25c..a85e1a2028360 100644 --- a/packages/@aws-cdk/aws-eks/package.json +++ b/packages/@aws-cdk/aws-eks/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-eks", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::EKS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-elasticache/package.json b/packages/@aws-cdk/aws-elasticache/package.json index 89383f1740d03..6ca05b0a97b07 100644 --- a/packages/@aws-cdk/aws-elasticache/package.json +++ b/packages/@aws-cdk/aws-elasticache/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-elasticache", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::ElastiCache", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-elasticbeanstalk/package.json b/packages/@aws-cdk/aws-elasticbeanstalk/package.json index e9a8f335bf315..6a2a639198bf4 100644 --- a/packages/@aws-cdk/aws-elasticbeanstalk/package.json +++ b/packages/@aws-cdk/aws-elasticbeanstalk/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-elasticbeanstalk", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::ElasticBeanstalk", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/package.json b/packages/@aws-cdk/aws-elasticloadbalancing/package.json index 27c4f6cf2e2b8..0635636e77849 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancing/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancing/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-elasticloadbalancing", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS ElasticLoadBalancing", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json index d262264d54ba3..dd88cead20ad7 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-elasticloadbalancingv2", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::ElasticLoadBalancingV2", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-elasticsearch/package.json b/packages/@aws-cdk/aws-elasticsearch/package.json index 60d17fa8a7d81..c7b9bc5287db2 100644 --- a/packages/@aws-cdk/aws-elasticsearch/package.json +++ b/packages/@aws-cdk/aws-elasticsearch/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-elasticsearch", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::Elasticsearch", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-emr/package.json b/packages/@aws-cdk/aws-emr/package.json index 2f07fe8982873..74ecde77b128d 100644 --- a/packages/@aws-cdk/aws-emr/package.json +++ b/packages/@aws-cdk/aws-emr/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-emr", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::EMR", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-events/package.json b/packages/@aws-cdk/aws-events/package.json index 537c5fc4b79d3..73ae2611289a1 100644 --- a/packages/@aws-cdk/aws-events/package.json +++ b/packages/@aws-cdk/aws-events/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-events", - "version": "0.8.1", + "version": "0.8.2", "description": "AWS CloudWatch Events Construct Library", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -47,15 +47,15 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1", - "@aws-cdk/util": "^0.8.1" + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2", + "@aws-cdk/util": "^0.8.2" }, "bundledDependencies": [ "@aws-cdk/util" diff --git a/packages/@aws-cdk/aws-gamelift/package.json b/packages/@aws-cdk/aws-gamelift/package.json index f26ab679d6118..161b690602afc 100644 --- a/packages/@aws-cdk/aws-gamelift/package.json +++ b/packages/@aws-cdk/aws-gamelift/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-gamelift", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::GameLift", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-glue/package.json b/packages/@aws-cdk/aws-glue/package.json index f2e14b4bd249a..d866c41a20ac2 100644 --- a/packages/@aws-cdk/aws-glue/package.json +++ b/packages/@aws-cdk/aws-glue/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-glue", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::Glue", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-guardduty/package.json b/packages/@aws-cdk/aws-guardduty/package.json index 6248ceb268119..fabead2f6f1dd 100644 --- a/packages/@aws-cdk/aws-guardduty/package.json +++ b/packages/@aws-cdk/aws-guardduty/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-guardduty", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::GuardDuty", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-iam/package.json b/packages/@aws-cdk/aws-iam/package.json index 509f4bc09acb0..b9b28326e483b 100644 --- a/packages/@aws-cdk/aws-iam/package.json +++ b/packages/@aws-cdk/aws-iam/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-iam", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK routines for easily assigning correct and minimal IAM permissions", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -48,14 +48,14 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-inspector/package.json b/packages/@aws-cdk/aws-inspector/package.json index d58c6d5f6ad02..739b74b96ec31 100644 --- a/packages/@aws-cdk/aws-inspector/package.json +++ b/packages/@aws-cdk/aws-inspector/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-inspector", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::Inspector", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-iot/package.json b/packages/@aws-cdk/aws-iot/package.json index fa2c9573610ba..f45013cfc3608 100644 --- a/packages/@aws-cdk/aws-iot/package.json +++ b/packages/@aws-cdk/aws-iot/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-iot", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::IoT", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-kinesis/package.json b/packages/@aws-cdk/aws-kinesis/package.json index 2b1093ef9f745..11f939a8ad06a 100644 --- a/packages/@aws-cdk/aws-kinesis/package.json +++ b/packages/@aws-cdk/aws-kinesis/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-kinesis", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS Kinesis", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -45,16 +45,16 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-kms": "^0.8.1", - "@aws-cdk/aws-logs": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-kms": "^0.8.2", + "@aws-cdk/aws-logs": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-kinesisanalytics/package.json b/packages/@aws-cdk/aws-kinesisanalytics/package.json index 89ac3e0058a02..09f3e18194d14 100644 --- a/packages/@aws-cdk/aws-kinesisanalytics/package.json +++ b/packages/@aws-cdk/aws-kinesisanalytics/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-kinesisanalytics", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::KinesisAnalytics", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-kinesisfirehose/package.json b/packages/@aws-cdk/aws-kinesisfirehose/package.json index e3275e0a26f4f..596f868a2650a 100644 --- a/packages/@aws-cdk/aws-kinesisfirehose/package.json +++ b/packages/@aws-cdk/aws-kinesisfirehose/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-kinesisfirehose", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::KinesisFirehose", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-kms/package.json b/packages/@aws-cdk/aws-kms/package.json index f26d46739f794..c32783c317c53 100644 --- a/packages/@aws-cdk/aws-kms/package.json +++ b/packages/@aws-cdk/aws-kms/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-kms", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS KMS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,15 +46,15 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 5a30b3a292d1d..a4cee9cbd8c36 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-lambda", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS Lambda", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -49,23 +49,23 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/assets": "^0.8.1", - "@aws-cdk/aws-cloudwatch": "^0.8.1", - "@aws-cdk/aws-codepipeline-api": "^0.8.1", - "@aws-cdk/aws-events": "^0.8.1", - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-logs": "^0.8.1", - "@aws-cdk/aws-s3": "^0.8.1", - "@aws-cdk/aws-s3-notifications": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1", - "@aws-cdk/cx-api": "^0.8.1" + "@aws-cdk/assets": "^0.8.2", + "@aws-cdk/aws-cloudwatch": "^0.8.2", + "@aws-cdk/aws-codepipeline-api": "^0.8.2", + "@aws-cdk/aws-events": "^0.8.2", + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-logs": "^0.8.2", + "@aws-cdk/aws-s3": "^0.8.2", + "@aws-cdk/aws-s3-notifications": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2", + "@aws-cdk/cx-api": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-logs/package.json b/packages/@aws-cdk/aws-logs/package.json index 1a925a32f4907..accaa50d35923 100644 --- a/packages/@aws-cdk/aws-logs/package.json +++ b/packages/@aws-cdk/aws-logs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-logs", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::Logs", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,15 +46,15 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-neptune/package.json b/packages/@aws-cdk/aws-neptune/package.json index e41a579c513bc..1ab1b355ba374 100644 --- a/packages/@aws-cdk/aws-neptune/package.json +++ b/packages/@aws-cdk/aws-neptune/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-neptune", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS Neptune", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -45,15 +45,15 @@ }, "license": "Apache-2.0", "devDependencies": { - "cdk-build-tools": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-ec2": "^0.8.1", - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-kms": "^0.8.1", - "@aws-cdk/aws-rds": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-ec2": "^0.8.2", + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-kms": "^0.8.2", + "@aws-cdk/aws-rds": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-opsworks/package.json b/packages/@aws-cdk/aws-opsworks/package.json index 5cb8c7f988f86..4027b6bf46788 100644 --- a/packages/@aws-cdk/aws-opsworks/package.json +++ b/packages/@aws-cdk/aws-opsworks/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-opsworks", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::OpsWorks", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-quickstarts/package.json b/packages/@aws-cdk/aws-quickstarts/package.json index 2afe746e20aad..d9e83c2f4367d 100644 --- a/packages/@aws-cdk/aws-quickstarts/package.json +++ b/packages/@aws-cdk/aws-quickstarts/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-quickstarts", - "version": "0.8.1", + "version": "0.8.2", "description": "AWS Quickstarts for the CDK", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -42,16 +42,16 @@ }, "license": "Apache-2.0", "devDependencies": { - "cdk-build-tools": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-cloudformation": "^0.8.1", - "@aws-cdk/aws-ec2": "^0.8.1", - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-rds": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1", - "@aws-cdk/util": "^0.8.1" + "@aws-cdk/aws-cloudformation": "^0.8.2", + "@aws-cdk/aws-ec2": "^0.8.2", + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-rds": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2", + "@aws-cdk/util": "^0.8.2" }, "bundledDependencies": [ "@aws-cdk/util" diff --git a/packages/@aws-cdk/aws-rds/package.json b/packages/@aws-cdk/aws-rds/package.json index 4ab256b802ba0..658b12a094d26 100644 --- a/packages/@aws-cdk/aws-rds/package.json +++ b/packages/@aws-cdk/aws-rds/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-rds", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS RDS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,17 +46,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-ec2": "^0.8.1", - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-kms": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-ec2": "^0.8.2", + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-kms": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-redshift/package.json b/packages/@aws-cdk/aws-redshift/package.json index 221a4dabd65f7..b1a2998a615df 100644 --- a/packages/@aws-cdk/aws-redshift/package.json +++ b/packages/@aws-cdk/aws-redshift/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-redshift", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::Redshift", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json index a823904aa8989..91f94e8688a09 100644 --- a/packages/@aws-cdk/aws-route53/package.json +++ b/packages/@aws-cdk/aws-route53/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-route53", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS Route53", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,17 +46,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", + "@aws-cdk/assert": "^0.8.2", "aws-sdk": "^2.259.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-ec2": "^0.8.1", - "@aws-cdk/aws-logs": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-ec2": "^0.8.2", + "@aws-cdk/aws-logs": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-s3-notifications/package.json b/packages/@aws-cdk/aws-s3-notifications/package.json index 390d43d9af0ba..4c7e187bd32af 100644 --- a/packages/@aws-cdk/aws-s3-notifications/package.json +++ b/packages/@aws-cdk/aws-s3-notifications/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-s3-notifications", - "version": "0.8.1", + "version": "0.8.2", "description": "Bucket Notifications API for AWS S3", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -43,11 +43,11 @@ }, "license": "Apache-2.0", "devDependencies": { - "cdk-build-tools": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-s3/package.json b/packages/@aws-cdk/aws-s3/package.json index 4490542d0cb4a..87379d48abb94 100644 --- a/packages/@aws-cdk/aws-s3/package.json +++ b/packages/@aws-cdk/aws-s3/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-s3", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS S3", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,17 +46,17 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-kms": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1", - "@aws-cdk/aws-s3-notifications": "^0.8.1" + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-kms": "^0.8.2", + "@aws-cdk/aws-s3-notifications": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-sdb/package.json b/packages/@aws-cdk/aws-sdb/package.json index d8f7dfe120e63..49454e84b56b6 100644 --- a/packages/@aws-cdk/aws-sdb/package.json +++ b/packages/@aws-cdk/aws-sdb/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-sdb", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::SDB", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-serverless/package.json b/packages/@aws-cdk/aws-serverless/package.json index 5eb5f72fe69db..bbc58d7b4b00c 100644 --- a/packages/@aws-cdk/aws-serverless/package.json +++ b/packages/@aws-cdk/aws-serverless/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-serverless", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::Serverless", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-servicecatalog/package.json b/packages/@aws-cdk/aws-servicecatalog/package.json index ca68c857b58a3..47d4d453c68a5 100644 --- a/packages/@aws-cdk/aws-servicecatalog/package.json +++ b/packages/@aws-cdk/aws-servicecatalog/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-servicecatalog", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::ServiceCatalog", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-servicediscovery/package.json b/packages/@aws-cdk/aws-servicediscovery/package.json index 5b1055d5233de..5dd0569bb64ba 100644 --- a/packages/@aws-cdk/aws-servicediscovery/package.json +++ b/packages/@aws-cdk/aws-servicediscovery/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-servicediscovery", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::ServiceDiscovery", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-ses/package.json b/packages/@aws-cdk/aws-ses/package.json index 030ae833ff897..067684f4924fc 100644 --- a/packages/@aws-cdk/aws-ses/package.json +++ b/packages/@aws-cdk/aws-ses/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-ses", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::SES", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-sns/package.json b/packages/@aws-cdk/aws-sns/package.json index 0894e119e8399..42f318b6a292f 100644 --- a/packages/@aws-cdk/aws-sns/package.json +++ b/packages/@aws-cdk/aws-sns/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-sns", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS SNS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,21 +46,21 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "@aws-cdk/aws-s3": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "@aws-cdk/aws-s3": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-cloudwatch": "^0.8.1", - "@aws-cdk/aws-events": "^0.8.1", - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-lambda": "^0.8.1", - "@aws-cdk/aws-sqs": "^0.8.1", - "@aws-cdk/aws-s3-notifications": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-cloudwatch": "^0.8.2", + "@aws-cdk/aws-events": "^0.8.2", + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-lambda": "^0.8.2", + "@aws-cdk/aws-s3-notifications": "^0.8.2", + "@aws-cdk/aws-sqs": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-sqs/package.json b/packages/@aws-cdk/aws-sqs/package.json index 73b1b18956fa5..79e721b269339 100644 --- a/packages/@aws-cdk/aws-sqs/package.json +++ b/packages/@aws-cdk/aws-sqs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-sqs", - "version": "0.8.1", + "version": "0.8.2", "description": "CDK Constructs for AWS SQS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,18 +46,18 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", + "@aws-cdk/assert": "^0.8.2", + "@aws-cdk/aws-s3": "^0.8.2", "aws-sdk": "^2.259.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1", - "@aws-cdk/aws-s3": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-kms": "^0.8.1", - "@aws-cdk/aws-s3-notifications": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-kms": "^0.8.2", + "@aws-cdk/aws-s3-notifications": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-ssm/package.json b/packages/@aws-cdk/aws-ssm/package.json index e00c4a0617be9..6ab415905782a 100644 --- a/packages/@aws-cdk/aws-ssm/package.json +++ b/packages/@aws-cdk/aws-ssm/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-ssm", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::SSM", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-stepfunctions/package.json b/packages/@aws-cdk/aws-stepfunctions/package.json index 6c1b31df6adea..c56a228d11f07 100644 --- a/packages/@aws-cdk/aws-stepfunctions/package.json +++ b/packages/@aws-cdk/aws-stepfunctions/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-stepfunctions", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::StepFunctions", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-waf/package.json b/packages/@aws-cdk/aws-waf/package.json index b916a52c8c37d..7344abf179258 100644 --- a/packages/@aws-cdk/aws-waf/package.json +++ b/packages/@aws-cdk/aws-waf/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-waf", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::WAF", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-wafregional/package.json b/packages/@aws-cdk/aws-wafregional/package.json index 64a1e7d8325cd..2a72114c45470 100644 --- a/packages/@aws-cdk/aws-wafregional/package.json +++ b/packages/@aws-cdk/aws-wafregional/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-wafregional", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::WAFRegional", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/aws-workspaces/package.json b/packages/@aws-cdk/aws-workspaces/package.json index bd4412a48a08e..09b73556d67f4 100644 --- a/packages/@aws-cdk/aws-workspaces/package.json +++ b/packages/@aws-cdk/aws-workspaces/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-workspaces", - "version": "0.8.1", + "version": "0.8.2", "description": "The CDK Construct Library for AWS::WorkSpaces", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -46,13 +46,13 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/cdk/package.json b/packages/@aws-cdk/cdk/package.json index cd83dbe58544c..49db8e68c8104 100644 --- a/packages/@aws-cdk/cdk/package.json +++ b/packages/@aws-cdk/cdk/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/cdk", - "version": "0.8.1", + "version": "0.8.2", "description": "AWS Cloud Development Kit Core Library", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -47,12 +47,12 @@ "license": "Apache-2.0", "devDependencies": { "@types/js-base64": "^2.3.1", - "cdk-build-tools": "^0.8.1", - "cfn2ts": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "cfn2ts": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cx-api": "^0.8.1", + "@aws-cdk/cx-api": "^0.8.2", "js-base64": "^2.4.5", "json-diff": "^0.3.1" }, diff --git a/packages/@aws-cdk/cfnspec/package.json b/packages/@aws-cdk/cfnspec/package.json index 288a5625b9c97..0e8ad10b145f3 100644 --- a/packages/@aws-cdk/cfnspec/package.json +++ b/packages/@aws-cdk/cfnspec/package.json @@ -1,7 +1,7 @@ { "name": "@aws-cdk/cfnspec", "description": "The CloudFormation resource specification used by @aws-cdk packages", - "version": "0.8.1", + "version": "0.8.2", "scripts": { "update": "/bin/bash build-tools/update.sh", "build": "cdk-build && node build-tools/build", @@ -21,11 +21,11 @@ "devDependencies": { "@types/fs-extra": "^4.0.8", "@types/md5": "^2.1.32", - "cdk-build-tools": "^0.8.1", + "cdk-build-tools": "^0.8.2", "fast-json-patch": "^2.0.6", "fs-extra": "^4.0.2", "json-diff": "^0.3.1", - "pkglint": "^0.8.1", + "pkglint": "^0.8.2", "sort-json": "^2.0.0" }, "dependencies": { diff --git a/packages/@aws-cdk/cloudformation-diff/package.json b/packages/@aws-cdk/cloudformation-diff/package.json index 27a230fed5482..daf4d823a1668 100644 --- a/packages/@aws-cdk/cloudformation-diff/package.json +++ b/packages/@aws-cdk/cloudformation-diff/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/cloudformation-diff", - "version": "0.8.1", + "version": "0.8.2", "description": "Utilities to diff CDK stacks against CloudFormation templates", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -23,13 +23,13 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-cdk/cfnspec": "^0.8.1", + "@aws-cdk/cfnspec": "^0.8.2", "colors": "^1.2.1", "source-map-support": "^0.5.6" }, "devDependencies": { - "cdk-build-tools": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "repository": { "url": "https://github.com/awslabs/aws-cdk.git", diff --git a/packages/@aws-cdk/cx-api/package.json b/packages/@aws-cdk/cx-api/package.json index 2ffb2f41f248f..4bfbd73ec1196 100644 --- a/packages/@aws-cdk/cx-api/package.json +++ b/packages/@aws-cdk/cx-api/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/cx-api", - "version": "0.8.1", + "version": "0.8.2", "description": "Cloud executable protocol", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -32,8 +32,8 @@ }, "license": "Apache-2.0", "devDependencies": { - "cdk-build-tools": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "repository": { "url": "https://github.com/awslabs/aws-cdk.git", diff --git a/packages/@aws-cdk/runtime-values/package.json b/packages/@aws-cdk/runtime-values/package.json index 8c633c9138b36..2e1a2960c2af7 100644 --- a/packages/@aws-cdk/runtime-values/package.json +++ b/packages/@aws-cdk/runtime-values/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/runtime-values", - "version": "0.8.1", + "version": "0.8.2", "description": "Runtime values support for the AWS CDK", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -42,18 +42,18 @@ }, "license": "Apache-2.0", "devDependencies": { - "@aws-cdk/assert": "^0.8.1", - "@aws-cdk/aws-ec2": "^0.8.1", - "@aws-cdk/aws-lambda": "^0.8.1", - "@aws-cdk/aws-sqs": "^0.8.1", - "cdk-build-tools": "^0.8.1", - "cdk-integ-tools": "^0.8.1", - "pkglint": "^0.8.1" + "@aws-cdk/assert": "^0.8.2", + "@aws-cdk/aws-ec2": "^0.8.2", + "@aws-cdk/aws-lambda": "^0.8.2", + "@aws-cdk/aws-sqs": "^0.8.2", + "cdk-build-tools": "^0.8.2", + "cdk-integ-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/aws-iam": "^0.8.1", - "@aws-cdk/aws-ssm": "^0.8.1", - "@aws-cdk/cdk": "^0.8.1" + "@aws-cdk/aws-iam": "^0.8.2", + "@aws-cdk/aws-ssm": "^0.8.2", + "@aws-cdk/cdk": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/@aws-cdk/util/package.json b/packages/@aws-cdk/util/package.json index 7ea44f8a121e5..6aae374a32c32 100644 --- a/packages/@aws-cdk/util/package.json +++ b/packages/@aws-cdk/util/package.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/util", - "version": "0.8.1", + "version": "0.8.2", "description": "Utility routines used for implementing CDK libraries", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -29,8 +29,8 @@ }, "license": "Apache-2.0", "devDependencies": { - "cdk-build-tools": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "homepage": "https://github.com/awslabs/aws-cdk" } diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index 700f1123c1399..9eed25f940f32 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -1,7 +1,7 @@ { "name": "aws-cdk", "description": "CDK Toolkit, the command line tool for CDK apps", - "version": "0.8.1", + "version": "0.8.2", "main": "lib/index.js", "types": "lib/index.d.ts", "bin": { @@ -39,14 +39,14 @@ "@types/uuid": "^3.4.3", "@types/yamljs": "^0.2.0", "@types/yargs": "^8.0.3", - "cdk-build-tools": "^0.8.1", + "cdk-build-tools": "^0.8.2", "mockery": "^2.1.0", - "pkglint": "^0.8.1" + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cloudformation-diff": "^0.8.1", - "@aws-cdk/cx-api": "^0.8.1", - "@aws-cdk/util": "^0.8.1", + "@aws-cdk/cloudformation-diff": "^0.8.2", + "@aws-cdk/cx-api": "^0.8.2", + "@aws-cdk/util": "^0.8.2", "archiver": "^2.1.1", "aws-sdk": "^2.259.1", "camelcase": "^5.0.0", diff --git a/packages/simple-resource-bundler/package.json b/packages/simple-resource-bundler/package.json index 44d7986bdcffa..967dbc8e90dc0 100644 --- a/packages/simple-resource-bundler/package.json +++ b/packages/simple-resource-bundler/package.json @@ -1,6 +1,6 @@ { "name": "simple-resource-bundler", - "version": "0.8.1", + "version": "0.8.2", "description": "Command-line tool to embed resources into JS libraries", "main": "bundler.js", "types": "bundler.d.ts", @@ -24,8 +24,8 @@ "devDependencies": { "@types/fs-extra": "^4.0.8", "@types/yargs": "^8.0.3", - "cdk-build-tools": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { "fs-extra": "^4.0.2", diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index d16f1c49c6c7e..672be3ba9354d 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -1,7 +1,7 @@ { "name": "cdk-build-tools", "private": true, - "version": "0.8.1", + "version": "0.8.2", "description": "Tools package with shared build scripts for CDK packages", "main": "index.js", "repository": { @@ -28,13 +28,13 @@ "devDependencies": { "@types/fs-extra": "^5.0.4", "@types/yargs": "^8.0.3", - "pkglint": "^0.8.1" + "pkglint": "^0.8.2" }, "dependencies": { "fs-extra": "^7.0.0", "jsii": "^0.6.4", "jsii-pacmak": "^0.6.4", - "merkle-build": "^0.8.1", + "merkle-build": "^0.8.2", "typescript": "^3.0.1", "yargs": "^9.0.1" }, diff --git a/tools/cdk-integ-tools/package.json b/tools/cdk-integ-tools/package.json index 375ca36e7f881..0f43dada0bf68 100644 --- a/tools/cdk-integ-tools/package.json +++ b/tools/cdk-integ-tools/package.json @@ -1,7 +1,7 @@ { "name": "cdk-integ-tools", "private": true, - "version": "0.8.1", + "version": "0.8.2", "description": "Package with integration test scripts for CDK packages", "main": "index.js", "repository": { @@ -27,13 +27,13 @@ "license": "Apache-2.0", "devDependencies": { "@types/yargs": "^8.0.3", - "cdk-build-tools": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { - "@aws-cdk/cloudformation-diff": "^0.8.1", - "@aws-cdk/cx-api": "^0.8.1", - "aws-cdk": "^0.8.1", + "@aws-cdk/cloudformation-diff": "^0.8.2", + "@aws-cdk/cx-api": "^0.8.2", + "aws-cdk": "^0.8.2", "yargs": "^9.0.1" }, "keywords": [ diff --git a/tools/cfn2ts/package.json b/tools/cfn2ts/package.json index 70b8dd4c8a9a7..b7a88d4ae5ac1 100644 --- a/tools/cfn2ts/package.json +++ b/tools/cfn2ts/package.json @@ -1,7 +1,7 @@ { "name": "cfn2ts", "private": true, - "version": "0.8.1", + "version": "0.8.2", "description": "Generates typescript types from CloudFormation spec, with support for enrichments", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -30,7 +30,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@aws-cdk/cfnspec": "^0.8.1", + "@aws-cdk/cfnspec": "^0.8.2", "codemaker": "^0.6.4", "colors": "^1.2.1", "fast-json-patch": "^2.0.6", @@ -41,8 +41,8 @@ "devDependencies": { "@types/fs-extra": "^4.0.8", "@types/yargs": "^8.0.3", - "cdk-build-tools": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "keywords": [ "aws", diff --git a/tools/merkle-build/package.json b/tools/merkle-build/package.json index c2730e3412935..b90d59a1edf20 100644 --- a/tools/merkle-build/package.json +++ b/tools/merkle-build/package.json @@ -1,7 +1,7 @@ { "name": "merkle-build", "private": true, - "version": "0.8.1", + "version": "0.8.2", "description": "Build tool helper for calculating hashes of a Merkle tree", "main": "lib/index.js", "types": "lib/index.ts", @@ -22,7 +22,7 @@ "license": "Apache-2.0", "devDependencies": { "@types/fs-extra": "^4.0.8", - "pkglint": "^0.8.1" + "pkglint": "^0.8.2" }, "dependencies": { "fs-extra": "^4.0.2" diff --git a/tools/pkglint/package.json b/tools/pkglint/package.json index 433e46b3b574f..201bfe2582e06 100644 --- a/tools/pkglint/package.json +++ b/tools/pkglint/package.json @@ -1,6 +1,6 @@ { "name": "pkglint", - "version": "0.8.1", + "version": "0.8.2", "private": true, "description": "Validate and fix package.json files", "main": "lib/index.js", diff --git a/tools/pkgtools/package.json b/tools/pkgtools/package.json index 5b235a415f456..4253fee65d292 100644 --- a/tools/pkgtools/package.json +++ b/tools/pkgtools/package.json @@ -1,7 +1,7 @@ { "name": "pkgtools", "private": true, - "version": "0.8.1", + "version": "0.8.2", "description": "Tools for generating cross-package artifacts", "main": "index.js", "repository": { @@ -28,8 +28,8 @@ "devDependencies": { "@types/fs-extra": "^4.0.8", "@types/yargs": "^8.0.3", - "cdk-build-tools": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "dependencies": { "fs-extra": "^4.0.2", diff --git a/tools/y-npm/package.json b/tools/y-npm/package.json index 4b1704ac63420..98ea3e4f208e8 100644 --- a/tools/y-npm/package.json +++ b/tools/y-npm/package.json @@ -1,6 +1,6 @@ { "name": "y-npm", - "version": "0.8.1", + "version": "0.8.2", "description": "Run npm commands using a local registry overlay", "private": true, "author": { @@ -35,8 +35,8 @@ "@types/colors": "^1.2.1", "@types/fs-extra": "^4.0.8", "@types/semver": "^5.5.0", - "cdk-build-tools": "^0.8.1", - "pkglint": "^0.8.1" + "cdk-build-tools": "^0.8.2", + "pkglint": "^0.8.2" }, "keywords": [ "aws", From 518cef37364d72078086911951050461f90c86d3 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Wed, 15 Aug 2018 12:47:37 -0700 Subject: [PATCH 24/33] refactor(aws-s3, aws-codepipeline): Align the S3 CodePipeline Action with other Actions. (#492) BREAKING CHANGE: move the S3 CodePipeline Action out of the aws-codepipeline package and into the aws-s3 package, and rename it from AmazonS3Source to PipelineSource. --- .../aws-codepipeline-api/lib/source-action.ts | 2 +- .../@aws-cdk/aws-codepipeline/lib/index.ts | 1 - .../test/integ.lambda-pipeline.ts | 2 +- .../test/integ.pipeline-cfn.ts | 2 +- .../test/test.general-validation.ts | 5 ++-- .../aws-codepipeline/test/test.pipeline.ts | 2 +- packages/@aws-cdk/aws-s3/README.md | 24 +++++++++++++++++++ packages/@aws-cdk/aws-s3/lib/index.ts | 1 + .../lib/pipeline-action.ts} | 12 +++++----- packages/@aws-cdk/aws-s3/package.json | 1 + 10 files changed, 38 insertions(+), 14 deletions(-) rename packages/@aws-cdk/{aws-codepipeline/lib/s3-source-action.ts => aws-s3/lib/pipeline-action.ts} (77%) diff --git a/packages/@aws-cdk/aws-codepipeline-api/lib/source-action.ts b/packages/@aws-cdk/aws-codepipeline-api/lib/source-action.ts index 3ccea318f59ec..beb3d193fb2c4 100644 --- a/packages/@aws-cdk/aws-codepipeline-api/lib/source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-api/lib/source-action.ts @@ -44,7 +44,7 @@ export interface SourceActionProps extends CommonActionProps { /** * Low-level class for source actions. * It is recommended that concrete types are used instead, - * such as {@link codepipeline.AmazonS3Source} or + * such as {@link s3.PipelineSource} or * {@link codecommit.PipelineSource}. */ export abstract class SourceAction extends Action { diff --git a/packages/@aws-cdk/aws-codepipeline/lib/index.ts b/packages/@aws-cdk/aws-codepipeline/lib/index.ts index 98b314f4f59aa..4875bcc768b76 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/index.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/index.ts @@ -1,7 +1,6 @@ export * from './github-source-action'; export * from './manual-approval-action'; export * from './pipeline'; -export * from './s3-source-action'; export * from './stage'; // AWS::CodePipeline CloudFormation Resources: diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.ts index 2ec41e9e8a5a9..c526fde753311 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.ts @@ -13,7 +13,7 @@ const sourceStage = new codepipeline.Stage(pipeline, 'Source'); const bucket = new s3.Bucket(stack, 'PipelineBucket', { versioned: true, }); -new codepipeline.AmazonS3Source(stack, 'Source', { +new s3.PipelineSource(stack, 'Source', { stage: sourceStage, artifactName: 'SourceArtifact', bucket, diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.ts index 6ff220dfc3fe5..68d7061d4fa80 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.ts @@ -16,7 +16,7 @@ const sourceStage = new codepipeline.Stage(pipeline, 'Source'); const bucket = new s3.Bucket(stack, 'PipelineBucket', { versioned: true, }); -const source = new codepipeline.AmazonS3Source(stack, 'Source', { +const source = new s3.PipelineSource(stack, 'Source', { stage: sourceStage, artifactName: 'SourceArtifact', bucket, diff --git a/packages/@aws-cdk/aws-codepipeline/test/test.general-validation.ts b/packages/@aws-cdk/aws-codepipeline/test/test.general-validation.ts index 655cc7d555792..bd924908ff957 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/test.general-validation.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.general-validation.ts @@ -3,7 +3,6 @@ import s3 = require('@aws-cdk/aws-s3'); import cdk = require('@aws-cdk/cdk'); import { Test } from 'nodeunit'; import { Pipeline } from '../lib/pipeline'; -import { AmazonS3Source } from '../lib/s3-source-action'; import { Stage } from '../lib/stage'; interface NameValidationTestCase { @@ -61,13 +60,13 @@ export = { const secondStage = new Stage(pipeline, 'SecondStage'); const bucket = new s3.Bucket(stack, 'PipelineBucket'); - new AmazonS3Source(stack, 'FirstAction', { + new s3.PipelineSource(stack, 'FirstAction', { stage: firstStage, artifactName: 'FirstArtifact', bucket, bucketKey: 'key', }); - new AmazonS3Source(stack, 'SecondAction', { + new s3.PipelineSource(stack, 'SecondAction', { stage: secondStage, artifactName: 'SecondAction', bucket, diff --git a/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts b/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts index 759a94ce50938..fb75b1e045595 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts @@ -137,7 +137,7 @@ export = { const pipeline = new codepipeline.Pipeline(stack, 'PL'); const stage1 = new codepipeline.Stage(pipeline, 'S1'); - new codepipeline.AmazonS3Source(stack, 'A1', { + new s3.PipelineSource(stack, 'A1', { stage: stage1, artifactName: 'Artifact', bucket: new s3.Bucket(stack, 'Bucket'), diff --git a/packages/@aws-cdk/aws-s3/README.md b/packages/@aws-cdk/aws-s3/README.md index 4b51848ed16e8..eaf25155b5b56 100644 --- a/packages/@aws-cdk/aws-s3/README.md +++ b/packages/@aws-cdk/aws-s3/README.md @@ -73,6 +73,30 @@ const policy = new BucketPolicy(this, 'MyBucketPolicy'); const bucket = new Bucket(this, 'MyBucket', { policy }); ``` +### Buckets as sources in CodePipeline + +This package also defines an Action that allows you to use a +Bucket as a source in CodePipeline: + +```ts +import codepipeline = require('@aws-cdk/aws-codepipeline'); +import s3 = require('@aws-cdk/aws-s3'); + +const sourceBucket = new s3.Bucket(this, 'MyBucket', { + versioned: true, // a Bucket used as a source in CodePipeline must be versioned +}); + +const pipeline = new codepipeline.Pipeline(this, 'MyPipeline'); +const sourceStage = new codepipeline.Stage(pipeline, 'Source'); +const sourceAction = new s3.PipelineSource(this, 'S3Source', { + stage: sourceStage, + bucket: sourceBucket, + bucketKey: 'path/to/file.zip', + artifactName: 'SourceOuptut', //name can be arbitrary +}); +// use sourceAction.artifact as the inputArtifact to later Actions... +``` + ### Importing and Exporting Buckets You can create a `Bucket` construct that represents an external/existing/unowned bucket by using the `Bucket.import` factory method. diff --git a/packages/@aws-cdk/aws-s3/lib/index.ts b/packages/@aws-cdk/aws-s3/lib/index.ts index 593c797757b3f..40bb9e593e176 100644 --- a/packages/@aws-cdk/aws-s3/lib/index.ts +++ b/packages/@aws-cdk/aws-s3/lib/index.ts @@ -1,5 +1,6 @@ export * from './bucket'; export * from './bucket-policy'; +export * from './pipeline-action'; export * from './rule'; // AWS::S3 CloudFormation Resources: diff --git a/packages/@aws-cdk/aws-codepipeline/lib/s3-source-action.ts b/packages/@aws-cdk/aws-s3/lib/pipeline-action.ts similarity index 77% rename from packages/@aws-cdk/aws-codepipeline/lib/s3-source-action.ts rename to packages/@aws-cdk/aws-s3/lib/pipeline-action.ts index d134810d76794..384b33fc012fd 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/s3-source-action.ts +++ b/packages/@aws-cdk/aws-s3/lib/pipeline-action.ts @@ -1,11 +1,11 @@ import actions = require('@aws-cdk/aws-codepipeline-api'); -import s3 = require('@aws-cdk/aws-s3'); import cdk = require('@aws-cdk/cdk'); +import { BucketRef } from './bucket'; /** - * Construction properties of the {@link AmazonS3Source S3 source action}. + * Construction properties of the {@link PipelineSource S3 source Action}. */ -export interface AmazonS3SourceProps extends actions.CommonActionProps { +export interface PipelineSourceProps extends actions.CommonActionProps { /** * The name of the source's output artifact. Output artifacts are used by CodePipeline as * inputs into other actions. @@ -15,7 +15,7 @@ export interface AmazonS3SourceProps extends actions.CommonActionProps { /** * The Amazon S3 bucket that stores the source code */ - bucket: s3.BucketRef; + bucket: BucketRef; /** * The key within the S3 bucket that stores the source code @@ -34,8 +34,8 @@ export interface AmazonS3SourceProps extends actions.CommonActionProps { /** * Source that is provided by a specific Amazon S3 object. */ -export class AmazonS3Source extends actions.SourceAction { - constructor(parent: cdk.Construct, name: string, props: AmazonS3SourceProps) { +export class PipelineSource extends actions.SourceAction { + constructor(parent: cdk.Construct, name: string, props: PipelineSourceProps) { super(parent, name, { stage: props.stage, provider: 'S3', diff --git a/packages/@aws-cdk/aws-s3/package.json b/packages/@aws-cdk/aws-s3/package.json index 87379d48abb94..18e0d44456e80 100644 --- a/packages/@aws-cdk/aws-s3/package.json +++ b/packages/@aws-cdk/aws-s3/package.json @@ -53,6 +53,7 @@ "pkglint": "^0.8.2" }, "dependencies": { + "@aws-cdk/aws-codepipeline-api": "^0.8.2", "@aws-cdk/aws-iam": "^0.8.2", "@aws-cdk/aws-kms": "^0.8.2", "@aws-cdk/aws-s3-notifications": "^0.8.2", From 0b9220278ef9e6f626bf1500a1b6b6691446a1c4 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Wed, 15 Aug 2018 13:50:41 -0700 Subject: [PATCH 25/33] refactor(aws-codepipeline): Pass the Pipeline to Stage through props instead of parent. (#568) BREAKING CHANGE: this commit changes the way we pass a Pipeline to a newly created Stage. Instead of passing it as a parent, in the first argument of the constructor, we now pass it through a separate props object, in the pipeline property. This is in order to make Stage more consistent with other Constructs, and with the changes made to the Actions API in #459. --- packages/@aws-cdk/aws-codebuild/README.md | 8 +++-- packages/@aws-cdk/aws-codecommit/README.md | 6 ++-- packages/@aws-cdk/aws-codepipeline/README.md | 12 +++---- .../@aws-cdk/aws-codepipeline/lib/pipeline.ts | 25 ++++++------- .../@aws-cdk/aws-codepipeline/lib/stage.ts | 35 ++++++++++++------- .../test/integ.cfn-template-from-repo.lit.ts | 4 +-- .../test/integ.lambda-pipeline.ts | 4 +-- .../test/integ.pipeline-cfn.ts | 4 +-- .../test/integ.pipeline-code-commit-build.ts | 4 +-- .../test/integ.pipeline-code-commit.ts | 4 +-- .../test/integ.pipeline-events.ts | 4 +-- .../aws-codepipeline/test/test.action.ts | 4 +-- .../test.cloudformation-pipeline-actions.ts | 10 +++--- .../test/test.general-validation.ts | 6 ++-- .../aws-codepipeline/test/test.pipeline.ts | 16 ++++----- packages/@aws-cdk/aws-lambda/README.md | 4 ++- packages/@aws-cdk/aws-s3/README.md | 4 ++- 17 files changed, 88 insertions(+), 66 deletions(-) diff --git a/packages/@aws-cdk/aws-codebuild/README.md b/packages/@aws-cdk/aws-codebuild/README.md index 7e96f68ca03ec..4408964272142 100644 --- a/packages/@aws-cdk/aws-codebuild/README.md +++ b/packages/@aws-cdk/aws-codebuild/README.md @@ -50,14 +50,18 @@ const project = new codebuild.PipelineProject(this, 'MyProject'); const pipeline = new codepipeline.Pipeline(this, 'MyPipeline'); -const sourceStage = new codepipeline.Stage(pipeline, 'Source'); +const sourceStage = new codepipeline.Stage(this, 'Source', { + pipeline, +}); const sourceAction = new codecommit.PipelineSource(this, 'CodeCommit', { stage: sourceStage, artifactName: 'SourceOutput', repository, }); -const buildStage = new codepipeline.Stage(pipeline, 'Build'); +const buildStage = new codepipeline.Stage(this, 'Build', { + pipeline, +}); new codebuild.PipelineBuildAction(this, 'CodeBuild', { stage: buildStage, inputArtifact: sourceAction.artifact, diff --git a/packages/@aws-cdk/aws-codecommit/README.md b/packages/@aws-cdk/aws-codecommit/README.md index d268be37f5b9b..13f58fe15e663 100644 --- a/packages/@aws-cdk/aws-codecommit/README.md +++ b/packages/@aws-cdk/aws-codecommit/README.md @@ -38,10 +38,12 @@ const repository = new codecommit.Repository( // ... const pipeline = new codepipeline.Pipeline(this, 'MyPipeline', { pipelineName: 'MyPipeline', }); -const sourceStage = new codepipeline.Stage(pipeline, 'Source'); +const sourceStage = new codepipeline.Stage(this, 'Source', { + pipeline, +})); const sourceAction = new codecommit.PipelineSource(this, 'CodeCommit', { stage: sourceStage, - artifactName: 'SourceOutput', //name can be arbitrary + artifactName: 'SourceOutput', // name can be arbitrary repository, }); // use sourceAction.artifact as the inputArtifact to later Actions... diff --git a/packages/@aws-cdk/aws-codepipeline/README.md b/packages/@aws-cdk/aws-codepipeline/README.md index e37b61d115e03..9cbb2d48b3dad 100644 --- a/packages/@aws-cdk/aws-codepipeline/README.md +++ b/packages/@aws-cdk/aws-codepipeline/README.md @@ -1,6 +1,6 @@ ## AWS CodePipeline construct library -Construct an empty pipeline: +Construct an empty Pipeline: ```ts const pipeline = new Pipeline(this, 'MyFirstPipeline', { @@ -8,15 +8,15 @@ const pipeline = new Pipeline(this, 'MyFirstPipeline', { }); ``` -All of the components of a pipeline are modeled as constructs. - -Append a stage to the pipeline: +Append a Stage to the Pipeline: ```ts -const sourceStage = new Stage(pipeline, 'Source'); +const sourceStage = new Stage(this, 'Source', { + pipeline, +}); ``` -Add an action to a stage: +Add an Action to a Stage: ```ts new codecommit.PipelineSource(this, 'Source', { diff --git a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts index 29a96cf5c0b7c..925b5db68ce10 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts @@ -211,21 +211,22 @@ export class Pipeline extends cdk.Construct implements events.IEventRuleTarget { } /** - * If a stage is added as a child, add it to the list of stages. - * TODO: This is a hack that should be removed once the CDK has an - * onChildAdded type hook. - * @override + * Adds a Stage to this Pipeline. + * This is an internal operation - + * a Stage is added to a Pipeline when it's constructed + * (the Pipeline is passed through the {@link StageProps#pipeline} property), + * so there is never a need to call this method explicitly. + * + * @param stage the newly created Stage to add to this Pipeline */ - protected addChild(child: cdk.Construct, name: string) { - super.addChild(child, name); - if (child instanceof Stage) { - this.appendStage(child); + public _addStage(stage: Stage): void { + // _addStage should be idempotent, in case a customer ever calls it directly + if (this.stages.includes(stage)) { + return; } - } - private appendStage(stage: Stage) { - if (this.stages.find(x => x.id === stage.id)) { - throw new Error(`A stage with name '${stage.id}' already exists`); + if (this.stages.find(x => x.name === stage.name)) { + throw new Error(`A stage with name '${stage.name}' already exists`); } this.stages.push(stage); diff --git a/packages/@aws-cdk/aws-codepipeline/lib/stage.ts b/packages/@aws-cdk/aws-codepipeline/lib/stage.ts index 8da2ac5d871dd..b2094f0ff3127 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/stage.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/stage.ts @@ -6,16 +6,29 @@ import { cloudformation } from './codepipeline.generated'; import { Pipeline } from './pipeline'; /** - * A stage in a pipeline. Stages are added to a pipeline by constructing a Stage with - * the pipeline as the first argument to the constructor. + * The construction properties for {@link Stage}. + */ +export interface StageProps { + /** + * The Pipeline to add the newly created Stage to. + */ + pipeline: Pipeline; +} + +/** + * A Stage in a Pipeline. + * Stages are added to a Pipeline by constructing a new Stage, + * and passing the Pipeline it belongs to through the {@link StageProps#pipeline} attribute. * * @example - * // add a stage to a pipeline - * new Stage(pipeline, 'MyStage'); + * // add a Stage to a Pipeline + * new Stage(this, 'MyStage', { + * pipeline: myPipeline, + * }); */ export class Stage extends cdk.Construct implements actions.IStage { /** - * The Pipeline this stage is a member of + * The Pipeline this Stage is a part of. */ public readonly pipeline: Pipeline; public readonly name: string; @@ -23,17 +36,15 @@ export class Stage extends cdk.Construct implements actions.IStage { private readonly _actions = new Array(); /** - * Append a new stage to the pipeline - * - * Only a Pipeline can be passed in as a parent because stages should - * always be attached to a pipeline. It's illogical to construct a Stage - * with any other parent. + * Create a new Stage. */ - constructor(parent: Pipeline, name: string) { + constructor(parent: cdk.Construct, name: string, props: StageProps) { super(parent, name); this.name = name; - this.pipeline = parent; + this.pipeline = props.pipeline; actions.validateName('Stage', name); + + this.pipeline._addStage(this); } /** diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.cfn-template-from-repo.lit.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.cfn-template-from-repo.lit.ts index d6587bf2e50a3..c9587e759eb97 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.cfn-template-from-repo.lit.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.cfn-template-from-repo.lit.ts @@ -13,7 +13,7 @@ const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); const repo = new codecommit.Repository(stack, 'TemplateRepo', { repositoryName: 'template-repo' }); -const sourceStage = new codepipeline.Stage(pipeline, 'Source'); +const sourceStage = new codepipeline.Stage(pipeline, 'Source', { pipeline }); const source = new codecommit.PipelineSource(stack, 'Source', { stage: sourceStage, repository: repo, @@ -21,7 +21,7 @@ const source = new codecommit.PipelineSource(stack, 'Source', { }); // Deployment stage: create and deploy changeset with manual approval -const prodStage = new codepipeline.Stage(pipeline, 'Deploy'); +const prodStage = new codepipeline.Stage(pipeline, 'Deploy', { pipeline }); const stackName = 'OurStack'; const changeSetName = 'StagedChangeSet'; diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.ts index c526fde753311..b67d284a95c81 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.lambda-pipeline.ts @@ -9,7 +9,7 @@ const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-lambda'); const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); -const sourceStage = new codepipeline.Stage(pipeline, 'Source'); +const sourceStage = new codepipeline.Stage(pipeline, 'Source', { pipeline }); const bucket = new s3.Bucket(stack, 'PipelineBucket', { versioned: true, }); @@ -29,7 +29,7 @@ const lambdaFun = new lambda.Function(stack, 'LambdaFun', { handler: 'index.handler', runtime: lambda.Runtime.NodeJS610, }); -const lambdaStage = new codepipeline.Stage(pipeline, 'Lambda'); +const lambdaStage = new codepipeline.Stage(pipeline, 'Lambda', { pipeline }); new lambda.PipelineInvokeAction(stack, 'Lambda', { stage: lambdaStage, lambda: lambdaFun, diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.ts index 68d7061d4fa80..96e8a438ff2d0 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.ts @@ -12,7 +12,7 @@ const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-cloudformation'); const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); -const sourceStage = new codepipeline.Stage(pipeline, 'Source'); +const sourceStage = new codepipeline.Stage(pipeline, 'Source', { pipeline }); const bucket = new s3.Bucket(stack, 'PipelineBucket', { versioned: true, }); @@ -23,7 +23,7 @@ const source = new s3.PipelineSource(stack, 'Source', { bucketKey: 'key', }); -const cfnStage = new codepipeline.Stage(pipeline, 'CFN'); +const cfnStage = new codepipeline.Stage(stack, 'CFN', { pipeline }); const changeSetName = "ChangeSetIntegTest"; const stackName = "IntegTest-TestActionStack"; diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.ts index a1d7a202d6cbc..a006fbe2f55e5 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit-build.ts @@ -13,7 +13,7 @@ const repository = new codecommit.Repository(stack, 'MyRepo', { const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); -const sourceStage = new codepipeline.Stage(pipeline, 'source'); +const sourceStage = new codepipeline.Stage(pipeline, 'source', { pipeline }); const source = new codecommit.PipelineSource(stack, 'source', { stage: sourceStage, artifactName: 'SourceArtifact', @@ -24,7 +24,7 @@ const project = new codebuild.Project(stack, 'MyBuildProject', { source: new codebuild.CodePipelineSource(), }); -const buildStage = new codepipeline.Stage(pipeline, 'build'); +const buildStage = new codepipeline.Stage(pipeline, 'build', { pipeline }); new codebuild.PipelineBuildAction(buildStage, 'build', { stage: buildStage, inputArtifact: source.artifact, diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit.ts index 967e2acfaad2d..43fabb7e9914f 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-code-commit.ts @@ -10,14 +10,14 @@ const repo = new codecommit.Repository(stack, 'MyRepo', { repositoryName: 'my-re const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); -const sourceStage = new codepipeline.Stage(pipeline, 'source'); +const sourceStage = new codepipeline.Stage(pipeline, 'source', { pipeline }); new codecommit.PipelineSource(stack, 'source', { stage: sourceStage, artifactName: 'SourceArtifact', repository: repo, }); -const buildStage = new codepipeline.Stage(pipeline, 'build'); +const buildStage = new codepipeline.Stage(stack, 'build', { pipeline }); new codepipeline.ManualApprovalAction(stack, 'manual', { stage: buildStage, }); diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.ts index 281517ff303f5..e19276f6fcfd1 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-events.ts @@ -11,8 +11,8 @@ const app = new cdk.App(process.argv); const stack = new cdk.Stack(app, 'aws-cdk-pipeline-event-target'); const pipeline = new codepipeline.Pipeline(stack, 'MyPipeline'); -const sourceStage = new codepipeline.Stage(pipeline, 'Source'); -const buildStage = new codepipeline.Stage(pipeline, 'Build'); +const sourceStage = new codepipeline.Stage(stack, 'Source', { pipeline }); +const buildStage = new codepipeline.Stage(stack, 'Build', { pipeline }); const repository = new codecommit.Repository(stack, 'CodeCommitRepo', { repositoryName: 'foo' diff --git a/packages/@aws-cdk/aws-codepipeline/test/test.action.ts b/packages/@aws-cdk/aws-codepipeline/test/test.action.ts index e1943e9973ec8..0881439e6b3e1 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/test.action.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.action.ts @@ -64,7 +64,7 @@ export = { 'standard action with artifacts'(test: Test) { const stack = new cdk.Stack(); const pipeline = new codepipeline.Pipeline(stack, 'pipeline'); - const stage = new codepipeline.Stage(pipeline, 'stage'); + const stage = new codepipeline.Stage(stack, 'stage', { pipeline }); const action = new TestAction(stack, 'TestAction', { stage, artifactBounds: actions.defaultBounds(), @@ -95,7 +95,7 @@ export = { function boundsValidationResult(numberOfArtifacts: number, min: number, max: number): string[] { const stack = new cdk.Stack(); const pipeline = new codepipeline.Pipeline(stack, 'pipeline'); - const stage = new codepipeline.Stage(pipeline, 'stage'); + const stage = new codepipeline.Stage(stack, 'stage', { pipeline }); const action = new TestAction(stack, 'TestAction', { stage, artifactBounds: actions.defaultBounds(), diff --git a/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts b/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts index 4a28eff000bc0..614c4d91dc1ae 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts @@ -24,7 +24,7 @@ export = { /** Source! */ const repo = new Repository(stack, 'MyVeryImportantRepo', { repositoryName: 'my-very-important-repo' }); - const sourceStage = new Stage(pipeline, 'source'); + const sourceStage = new Stage(pipeline, 'source', { pipeline }); const source = new PipelineSource(stack, 'source', { stage: sourceStage, @@ -34,7 +34,7 @@ export = { /** Build! */ - const buildStage = new Stage(pipeline, 'build'); + const buildStage = new Stage(pipeline, 'build', { pipeline }); const buildArtifacts = new CodePipelineBuildArtifacts(); const project = new Project(stack, 'MyBuildProject', { source: new CodePipelineSource(), @@ -53,7 +53,7 @@ export = { // To execute a change set - yes, you probably do need *:* 🤷‍♀️ changeSetExecRole.addToPolicy(new PolicyStatement().addAllResources().addAction("*")); - const prodStage = new Stage(pipeline, 'prod'); + const prodStage = new Stage(stack, 'prod', { pipeline }); const stackName = 'BrelandsStack'; const changeSetName = 'MyMagicalChangeSet'; @@ -366,8 +366,8 @@ class TestFixture extends cdk.Stack { super(); this.pipeline = new Pipeline(this, 'Pipeline'); - this.sourceStage = new Stage(this.pipeline, 'Source'); - this.deployStage = new Stage(this.pipeline, 'Deploy'); + this.sourceStage = new Stage(this.pipeline, 'Source', { pipeline: this.pipeline }); + this.deployStage = new Stage(this.pipeline, 'Deploy', { pipeline: this.pipeline }); this.repo = new Repository(this, 'MyVeryImportantRepo', { repositoryName: 'my-very-important-repo' }); this.source = new PipelineSource(this, 'Source', { stage: this.sourceStage, diff --git a/packages/@aws-cdk/aws-codepipeline/test/test.general-validation.ts b/packages/@aws-cdk/aws-codepipeline/test/test.general-validation.ts index bd924908ff957..226007a243e0d 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/test.general-validation.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.general-validation.ts @@ -56,8 +56,8 @@ export = { 'should fail if Pipeline has a Source Action in a non-first Stage'(test: Test) { const stack = new cdk.Stack(); const pipeline = new Pipeline(stack, 'Pipeline'); - const firstStage = new Stage(pipeline, 'FirstStage'); - const secondStage = new Stage(pipeline, 'SecondStage'); + const firstStage = new Stage(stack, 'FirstStage', { pipeline }); + const secondStage = new Stage(stack, 'SecondStage', { pipeline }); const bucket = new s3.Bucket(stack, 'PipelineBucket'); new s3.PipelineSource(stack, 'FirstAction', { @@ -83,5 +83,5 @@ export = { function stageForTesting(): Stage { const stack = new cdk.Stack(); const pipeline = new Pipeline(stack, 'pipeline'); - return new Stage(pipeline, 'stage'); + return new Stage(stack, 'stage', { pipeline }); } diff --git a/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts b/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts index fb75b1e045595..5bcff6d224cb2 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts @@ -19,14 +19,14 @@ export = { }); const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); - const sourceStage = new codepipeline.Stage(pipeline, 'source'); + const sourceStage = new codepipeline.Stage(pipeline, 'source', { pipeline }); const source = new codecommit.PipelineSource(stack, 'source', { stage: sourceStage, artifactName: 'SourceArtifact', repository, }); - const buildStage = new codepipeline.Stage(pipeline, 'build'); + const buildStage = new codepipeline.Stage(pipeline, 'build', { pipeline }); const project = new codebuild.Project(stack, 'MyBuildProject', { source: new codebuild.CodePipelineSource() }); @@ -48,7 +48,7 @@ export = { const p = new codepipeline.Pipeline(stack, 'P'); - const s1 = new codepipeline.Stage(p, 'Source'); + const s1 = new codepipeline.Stage(stack, 'Source', { pipeline: p }); new codepipeline.GitHubSource(stack, 'GH', { stage: s1, artifactName: 'A', @@ -58,7 +58,7 @@ export = { repo: 'bar' }); - const s2 = new codepipeline.Stage(p, 'Two'); + const s2 = new codepipeline.Stage(stack, 'Two', { pipeline: p }); new codepipeline.ManualApprovalAction(stack, 'Boo', { stage: s2 }); expect(stack).to(haveResource('AWS::CodePipeline::Pipeline', { @@ -136,7 +136,7 @@ export = { const pipeline = new codepipeline.Pipeline(stack, 'PL'); - const stage1 = new codepipeline.Stage(pipeline, 'S1'); + const stage1 = new codepipeline.Stage(stack, 'S1', { pipeline }); new s3.PipelineSource(stack, 'A1', { stage: stage1, artifactName: 'Artifact', @@ -144,7 +144,7 @@ export = { bucketKey: 'Key' }); - const stage2 = new codepipeline.Stage(pipeline, 'S2'); + const stage2 = new codepipeline.Stage(stack, 'S2', { pipeline }); new codepipeline.ManualApprovalAction(stack, 'A2', { stage: stage2 }); pipeline.onStateChange('OnStateChange', topic, { @@ -262,7 +262,7 @@ export = { }); const pipeline = new codepipeline.Pipeline(stack, 'Pipeline'); - const stage = new codepipeline.Stage(pipeline, 'Stage'); + const stage = new codepipeline.Stage(stack, 'Stage', { pipeline }); new lambda.PipelineInvokeAction(stack, 'InvokeAction', { stage, lambda: lambdaFun, @@ -365,7 +365,7 @@ export = { function stageForTesting(stack: cdk.Stack): codepipeline.Stage { const pipeline = new codepipeline.Pipeline(stack, 'pipeline'); - return new codepipeline.Stage(pipeline, 'stage'); + return new codepipeline.Stage(pipeline, 'stage', { pipeline }); } function repositoryForTesting(stack: cdk.Stack): codecommit.Repository { diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index 759f22beadf86..8956bf1af702c 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -49,7 +49,9 @@ const lambdaFun = new lambda.Function(this, 'MyLambda', { }); const pipeline = new codepipeline.Pipeline(this, 'MyPipeline'); -const lambdaStage = new codepipeline.Stage(pipeline, 'Lambda'); +const lambdaStage = new codepipeline.Stage(this, 'Lambda', { + pipeline, +}); new lambda.PipelineInvokeAction(this, 'Lambda', { stage: lambdaStage, lambda: lambdaFun, diff --git a/packages/@aws-cdk/aws-s3/README.md b/packages/@aws-cdk/aws-s3/README.md index eaf25155b5b56..a9a5c1bf826dd 100644 --- a/packages/@aws-cdk/aws-s3/README.md +++ b/packages/@aws-cdk/aws-s3/README.md @@ -87,7 +87,9 @@ const sourceBucket = new s3.Bucket(this, 'MyBucket', { }); const pipeline = new codepipeline.Pipeline(this, 'MyPipeline'); -const sourceStage = new codepipeline.Stage(pipeline, 'Source'); +const sourceStage = new codepipeline.Stage(this, 'Source', { + pipeline, +}); const sourceAction = new s3.PipelineSource(this, 'S3Source', { stage: sourceStage, bucket: sourceBucket, From 0220bbc2ec034573fbb8c5bc3d2254b4df9c5b5c Mon Sep 17 00:00:00 2001 From: James Cooke Date: Thu, 16 Aug 2018 06:09:48 +0100 Subject: [PATCH 26/33] fix(aws-rds): unable to create cluster with a single instance (#578) The docs state that you should be able to create a cluster with a single instance however there is a trivial error that prevents this. --- packages/@aws-cdk/aws-rds/lib/cluster.ts | 2 +- .../@aws-cdk/aws-rds/test/test.cluster.ts | 32 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-rds/lib/cluster.ts b/packages/@aws-cdk/aws-rds/lib/cluster.ts index 0759ed52d7753..80ef230dbd255 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster.ts @@ -177,7 +177,7 @@ export class DatabaseCluster extends DatabaseClusterRef { this.readerEndpoint = new Endpoint(cluster.dbClusterReadEndpointAddress, cluster.dbClusterEndpointPort); const instanceCount = props.instances != null ? props.instances : 2; - if (instanceCount <= 1) { + if (instanceCount < 1) { throw new Error('At least one instance is required'); } diff --git a/packages/@aws-cdk/aws-rds/test/test.cluster.ts b/packages/@aws-cdk/aws-rds/test/test.cluster.ts index 3db7d3e3818c6..c753d8a848606 100644 --- a/packages/@aws-cdk/aws-rds/test/test.cluster.ts +++ b/packages/@aws-cdk/aws-rds/test/test.cluster.ts @@ -57,7 +57,37 @@ export = { // THEN: No error test.done(); - } + }, + 'can create a cluster with a single instance'(test: Test) { + // GIVEN + const stack = testStack(); + const vpc = new ec2.VpcNetwork(stack, 'VPC'); + + // WHEN + new DatabaseCluster(stack, 'Database', { + engine: DatabaseClusterEngine.Aurora, + instances: 1, + masterUser: { + username: new Username('admin'), + password: new Password('tooshort'), + }, + instanceProps: { + instanceType: new ec2.InstanceTypePair(ec2.InstanceClass.Burstable2, ec2.InstanceSize.Small), + vpc + } + }); + + // THEN + expect(stack).to(haveResource('AWS::RDS::DBCluster', { + Engine: "aurora", + DBSubnetGroupName: { Ref: "DatabaseSubnets56F17B9A" }, + MasterUsername: "admin", + MasterUserPassword: "tooshort", + VpcSecurityGroupIds: [ {"Fn::GetAtt": ["DatabaseSecurityGroup5C91FDCB", "GroupId"]}] + })); + + test.done(); + }, }; function testStack() { From 61f26dd7e6f20e1614dd7d810656aaa988e8283f Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 16 Aug 2018 12:59:16 +0200 Subject: [PATCH 27/33] feat(aws-ec2): add InstanceSize.Nano (#581) Fixes #577. --- packages/@aws-cdk/aws-ec2/lib/instance-types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@aws-cdk/aws-ec2/lib/instance-types.ts b/packages/@aws-cdk/aws-ec2/lib/instance-types.ts index 4058e0bf2dfb3..07b1350478f23 100644 --- a/packages/@aws-cdk/aws-ec2/lib/instance-types.ts +++ b/packages/@aws-cdk/aws-ec2/lib/instance-types.ts @@ -193,6 +193,7 @@ export enum InstanceClass { * What size of instance to use */ export enum InstanceSize { + None = 'nano', Micro = 'micro', Small = 'small', Medium = 'medium', From 5812a14bfb9cb604c16445980dd8be678d7a6f6b Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 16 Aug 2018 17:10:30 +0200 Subject: [PATCH 28/33] BREAKING(aws-ec2): SecurityGroup can be used in Connections (#582) Refactoring of the object model for connection/security groups so that a SecurityGroup object can be used as the target of an .allowTo() statement: cluster.connections.allowTo(securityGroup) SecurityGroupRef is now abstract and needs to be constructed using SecurityGroupRef.import(). This is also the mechanism for importing a non-constructed SecurityGroup into the construct tree. As part of the refactoring: - Get rid of IDefaultConnectable, the functionality has been folded into IConnectable/Connections. - Get rid of ISecurityGroup. - Rename IConnectionPeer => ISecurityGroupRule. - Drastically simplify implementation, get rid of recursion and classes to enable the recursion to terminate. All complex logic is now nicely contained within Connections. This change is BREAKING to connections-enabled construct writers, but transparent to application builders. Fixes #579. --- .../aws-ec2/lib/auto-scaling-group.ts | 11 +- packages/@aws-cdk/aws-ec2/lib/connections.ts | 154 ++++++++---------- packages/@aws-cdk/aws-ec2/lib/index.ts | 2 +- .../@aws-cdk/aws-ec2/lib/load-balancer.ts | 31 ++-- .../{connection.ts => security-group-rule.ts} | 28 ++-- .../@aws-cdk/aws-ec2/lib/security-group.ts | 76 ++++++--- .../@aws-cdk/aws-ec2/test/test.connections.ts | 46 +++++- .../@aws-cdk/aws-quickstarts/lib/database.ts | 10 +- packages/@aws-cdk/aws-quickstarts/lib/rdgw.ts | 9 +- packages/@aws-cdk/aws-rds/lib/cluster-ref.ts | 12 +- packages/@aws-cdk/aws-rds/lib/cluster.ts | 9 +- 11 files changed, 211 insertions(+), 177 deletions(-) rename packages/@aws-cdk/aws-ec2/lib/{connection.ts => security-group-rule.ts} (84%) diff --git a/packages/@aws-cdk/aws-ec2/lib/auto-scaling-group.ts b/packages/@aws-cdk/aws-ec2/lib/auto-scaling-group.ts index e59c706822cfa..2da8662dff415 100644 --- a/packages/@aws-cdk/aws-ec2/lib/auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-ec2/lib/auto-scaling-group.ts @@ -2,12 +2,12 @@ import autoscaling = require('@aws-cdk/aws-autoscaling'); import iam = require('@aws-cdk/aws-iam'); import sns = require('@aws-cdk/aws-sns'); import cdk = require('@aws-cdk/cdk'); -import { AllConnections, AnyIPv4, IConnectionPeer } from './connection'; -import { Connections } from './connections'; +import { Connections, IConnectable } from './connections'; import { InstanceType } from './instance-types'; import { ClassicLoadBalancer, IClassicLoadBalancerTarget } from './load-balancer'; import { IMachineImageSource, OperatingSystemType } from './machine-image'; import { SecurityGroup } from './security-group'; +import { AllConnections, AnyIPv4 } from './security-group-rule'; import { VpcNetworkRef, VpcPlacementStrategy } from './vpc-ref'; /** @@ -83,9 +83,7 @@ export interface AutoScalingGroupProps { * * The ASG spans all availability zones. */ -export class AutoScalingGroup extends cdk.Construct implements IClassicLoadBalancerTarget { - public readonly connectionPeer: IConnectionPeer; - +export class AutoScalingGroup extends cdk.Construct implements IClassicLoadBalancerTarget, IConnectable { /** * The type of OS instances of this fleet are running. */ @@ -110,8 +108,7 @@ export class AutoScalingGroup extends cdk.Construct implements IClassicLoadBalan super(parent, name); this.securityGroup = new SecurityGroup(this, 'InstanceSecurityGroup', { vpc: props.vpc }); - this.connections = new Connections(this.securityGroup); - this.connectionPeer = this.securityGroup; + this.connections = new Connections({ securityGroup: this.securityGroup }); if (props.allowAllOutbound !== false) { this.connections.allowTo(new AnyIPv4(), new AllConnections(), 'Outbound traffic allowed by default'); diff --git a/packages/@aws-cdk/aws-ec2/lib/connections.ts b/packages/@aws-cdk/aws-ec2/lib/connections.ts index d7eaca75ae605..6bcdd5fe2975e 100644 --- a/packages/@aws-cdk/aws-ec2/lib/connections.ts +++ b/packages/@aws-cdk/aws-ec2/lib/connections.ts @@ -1,6 +1,5 @@ -import { AnyIPv4, IConnectionPeer, IPortRange } from "./connection"; -import { SecurityGroupId } from "./ec2.generated"; -import { ISecurityGroup } from "./security-group"; +import { SecurityGroupRef } from "./security-group"; +import { AnyIPv4, IPortRange, ISecurityGroupRule } from "./security-group-rule"; /** * The goal of this module is to make possible to write statements like this: @@ -24,33 +23,56 @@ export interface IConnectable { } /** - * An object that has a Connections object as well as a default port range. + * Properties to intialize a new Connections object */ -export interface IDefaultConnectable extends IConnectable { - readonly defaultPortRange: IPortRange; +export interface ConnectionsProps { + /** + * Class that represents the rule by which others can connect to this connectable + * + * This object is required, but will be derived from securityGroup if that is passed. + * + * @default Derived from securityGroup if set. + */ + securityGroupRule?: ISecurityGroupRule; + + /** + * What securityGroup this object is managing connections for + * + * @default No security + */ + securityGroup?: SecurityGroupRef; + + /** + * Default port range for initiating connections to and from this object + * + * @default No default port range + */ + defaultPortRange?: IPortRange; } /** - * Manage the security group (firewall) for a connectable resource. + * Manage the allowed network connections for constructs with Security Groups. * - * This object contains method to allow connections between objects - * that can allow connections. + * Security Groups can be thought of as a firewall for network-connected + * devices. This class makes it easy to allow network connections to and + * from security groups, and between security groups individually. When + * establishing connectivity between security groups, it will automatically + * add rules in both security groups * - * The .allowDefaultPortXxx() methods are only available if the resource - * this object was created for has the concept of a default port range. */ export class Connections { - public readonly connectionPeer: IConnectionPeer; + private readonly securityGroupRule: ISecurityGroupRule; + private readonly securityGroup?: SecurityGroupRef; + private readonly defaultPortRange?: IPortRange; - constructor(private readonly securityGroup: ISecurityGroup, private readonly defaultPortRange?: IPortRange) { - this.connectionPeer = securityGroup; - } + constructor(props: ConnectionsProps) { + if (!props.securityGroupRule && !props.securityGroup) { + throw new Error('Connections: require one of securityGroupRule or securityGroup'); + } - /** - * Allow connections to the peer on their default port - */ - public allowToDefaultPort(other: IDefaultConnectable, description: string) { - this.allowTo(other, other.defaultPortRange, description); + this.securityGroupRule = props.securityGroupRule || props.securityGroup!; + this.securityGroup = props.securityGroup; + this.defaultPortRange = props.defaultPortRange; } /** @@ -58,12 +80,12 @@ export class Connections { */ public allowTo(other: IConnectable, portRange: IPortRange, description: string) { if (this.securityGroup) { - this.securityGroup.addEgressRule(other.connections.connectionPeer, portRange, description); + this.securityGroup.addEgressRule(other.connections.securityGroupRule, portRange, description); + } + if (other.connections.securityGroup) { + other.connections.securityGroup.addIngressRule(this.securityGroupRule, portRange, description); + } - other.connections.allowFrom( - new NullConnectable(this.connectionPeer), - portRange, - description); } /** @@ -71,12 +93,11 @@ export class Connections { */ public allowFrom(other: IConnectable, portRange: IPortRange, description: string) { if (this.securityGroup) { - this.securityGroup.addIngressRule(other.connections.connectionPeer, portRange, description); + this.securityGroup.addIngressRule(other.connections.securityGroupRule, portRange, description); + } + if (other.connections.securityGroup) { + other.connections.securityGroup.addEgressRule(this.securityGroupRule, portRange, description); } - other.connections.allowTo( - new NullConnectable(this.connectionPeer), - portRange, - description); } /** @@ -84,21 +105,21 @@ export class Connections { */ public allowInternally(portRange: IPortRange, description: string) { if (this.securityGroup) { - this.securityGroup.addIngressRule(this.securityGroup, portRange, description); + this.securityGroup.addIngressRule(this.securityGroupRule, portRange, description); } } /** * Allow to all IPv4 ranges */ - public allowToAnyIpv4(portRange: IPortRange, description: string) { + public allowToAnyIPv4(portRange: IPortRange, description: string) { this.allowTo(new AnyIPv4(), portRange, description); } /** * Allow from any IPv4 ranges */ - public allowFromAnyIpv4(portRange: IPortRange, description: string) { + public allowFromAnyIPv4(portRange: IPortRange, description: string) { this.allowFrom(new AnyIPv4(), portRange, description); } @@ -109,7 +130,7 @@ export class Connections { */ public allowDefaultPortFrom(other: IConnectable, description: string) { if (!this.defaultPortRange) { - throw new Error('Cannot call allowDefaultPortFrom(): resource has no default port'); + throw new Error('Cannot call allowDefaultPortFrom(): this resource has no default port'); } this.allowFrom(other, this.defaultPortRange, description); } @@ -119,7 +140,7 @@ export class Connections { */ public allowDefaultPortInternally(description: string) { if (!this.defaultPortRange) { - throw new Error('Cannot call allowDefaultPortInternally(): resource has no default port'); + throw new Error('Cannot call allowDefaultPortInternally(): this resource has no default port'); } this.allowInternally(this.defaultPortRange, description); } @@ -129,62 +150,19 @@ export class Connections { */ public allowDefaultPortFromAnyIpv4(description: string) { if (!this.defaultPortRange) { - throw new Error('Cannot call allowDefaultPortFromAnyIpv4(): resource has no default port'); + throw new Error('Cannot call allowDefaultPortFromAnyIpv4(): this resource has no default port'); } - this.allowFromAnyIpv4(this.defaultPortRange, description); - } -} - -/** - * Connectable that represents a peer but doesn't modify any security groups - */ -class NullConnectable implements IConnectable { - public readonly connections: Connections; - - constructor(connectionPeer: IConnectionPeer) { - this.connections = new SecurityGrouplessConnections(connectionPeer); - } -} - -/** - * This object is used by peers who don't allow reverse connections. - */ -export class SecurityGrouplessConnections extends Connections { - constructor(public readonly connectionPeer: IConnectionPeer) { - // Because Connections is no longer an interface but a concrete class, - // we must inherit from it and create it with an instance of ISecurityGroup. - super(new NullSecurityGroup()); - } - - public allowTo(_other: IConnectable, _connection: IPortRange, _description: string): void { - // Nothing to do + this.allowFromAnyIPv4(this.defaultPortRange, description); } - public allowFrom(_other: IConnectable, _connection: IPortRange, _description: string): void { - // Nothing to do - } -} - -/** - * Instance of ISecurityGroup that's only there for show. - */ -class NullSecurityGroup implements ISecurityGroup { - public securityGroupId: SecurityGroupId = new SecurityGroupId(); - public canInlineRule: boolean = false; - - public addIngressRule(_peer: IConnectionPeer, _connection: IPortRange, _description: string): void { - // Nothing - } - - public addEgressRule(_peer: IConnectionPeer, _connection: IPortRange, _description: string): void { - // Nothing - } - - public toIngressRuleJSON() { - return {}; - } + /** + * Allow connections to the security group on their default port + */ + public allowToDefaultPort(other: IConnectable, description: string) { + if (other.connections.defaultPortRange === undefined) { + throw new Error('Cannot call alloToDefaultPort(): other resource has no default port'); + } - public toEgressRuleJSON() { - return {}; + this.allowTo(other, other.connections.defaultPortRange, description); } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2/lib/index.ts b/packages/@aws-cdk/aws-ec2/lib/index.ts index 5008448df605c..c293d73b5da50 100644 --- a/packages/@aws-cdk/aws-ec2/lib/index.ts +++ b/packages/@aws-cdk/aws-ec2/lib/index.ts @@ -1,10 +1,10 @@ export * from './auto-scaling-group'; -export * from './connection'; export * from './connections'; export * from './instance-types'; export * from './load-balancer'; export * from './machine-image'; export * from './security-group'; +export * from './security-group-rule'; export * from './vpc'; export * from './vpc-ref'; diff --git a/packages/@aws-cdk/aws-ec2/lib/load-balancer.ts b/packages/@aws-cdk/aws-ec2/lib/load-balancer.ts index 2a84245202b74..f4436294e69fa 100644 --- a/packages/@aws-cdk/aws-ec2/lib/load-balancer.ts +++ b/packages/@aws-cdk/aws-ec2/lib/load-balancer.ts @@ -1,8 +1,8 @@ import elasticloadbalancing = require('@aws-cdk/aws-elasticloadbalancing'); import cdk = require('@aws-cdk/cdk'); -import { AnyIPv4, IConnectionPeer, IPortRange, TcpPort } from './connection'; -import { Connections, IConnectable, IDefaultConnectable } from './connections'; -import { ISecurityGroup, SecurityGroup } from './security-group'; +import { Connections, IConnectable } from './connections'; +import { SecurityGroup, SecurityGroupRef } from './security-group'; +import { AnyIPv4, IPortRange, TcpPort } from './security-group-rule'; import { VpcNetworkRef, VpcSubnetRef } from './vpc-ref'; /** @@ -193,8 +193,6 @@ export class ClassicLoadBalancer extends cdk.Construct implements IConnectable { */ public readonly connections: Connections; - public readonly connectionPeer: IConnectionPeer; - /** * An object controlling specifically the connections for each listener added to this load balancer */ @@ -211,8 +209,7 @@ export class ClassicLoadBalancer extends cdk.Construct implements IConnectable { super(parent, name); this.securityGroup = new SecurityGroup(this, 'SecurityGroup', { vpc: props.vpc }); - this.connections = new Connections(this.securityGroup); - this.connectionPeer = this.securityGroup; + this.connections = new Connections({ securityGroup: this.securityGroup }); // Depending on whether the ELB has public or internal IPs, pick the right backend subnets const subnets: VpcSubnetRef[] = props.internetFacing ? props.vpc.publicSubnets : props.vpc.privateSubnets; @@ -323,17 +320,23 @@ export class ClassicLoadBalancer extends cdk.Construct implements IConnectable { } /** - * Reference to a listener's port just created + * Reference to a listener's port just created. + * + * This implements IConnectable with a default port (the port that an ELB + * listener was just created on) for a given security group so that it can be + * conveniently used just like any Connectable. E.g: + * + * const listener = elb.addListener(...); * - * This class exists to make it convenient to add port ranges to the load - * balancer's security group just for the port ranges that are involved in the - * listener. + * listener.connections.allowDefaultPortFromAnyIPv4(); + * // or + * instance.connections.allowToDefaultPort(listener); */ -export class ClassicListenerPort implements IDefaultConnectable { +export class ClassicListenerPort implements IConnectable { public readonly connections: Connections; - constructor(securityGroup: ISecurityGroup, public readonly defaultPortRange: IPortRange) { - this.connections = new Connections(securityGroup, defaultPortRange); + constructor(securityGroup: SecurityGroupRef, defaultPortRange: IPortRange) { + this.connections = new Connections({ securityGroup, defaultPortRange }); } } diff --git a/packages/@aws-cdk/aws-ec2/lib/connection.ts b/packages/@aws-cdk/aws-ec2/lib/security-group-rule.ts similarity index 84% rename from packages/@aws-cdk/aws-ec2/lib/connection.ts rename to packages/@aws-cdk/aws-ec2/lib/security-group-rule.ts index 2b6c4b69b3ebf..013f30b6d446a 100644 --- a/packages/@aws-cdk/aws-ec2/lib/connection.ts +++ b/packages/@aws-cdk/aws-ec2/lib/security-group-rule.ts @@ -1,10 +1,10 @@ import { Token } from "@aws-cdk/cdk"; -import { Connections, IConnectable, SecurityGrouplessConnections } from "./connections"; +import { Connections, IConnectable } from "./connections"; /** * Interface for classes that provide the peer-specification parts of a security group rule */ -export interface IConnectionPeer { +export interface ISecurityGroupRule { /** * Whether the rule can be inlined into a SecurityGroup or not */ @@ -24,12 +24,11 @@ export interface IConnectionPeer { /** * A connection to and from a given IP range */ -export class CidrIp implements IConnectionPeer, IConnectable { +export class CidrIPv4 implements ISecurityGroupRule, IConnectable { public readonly canInlineRule = true; - public readonly connections: Connections; + public readonly connections: Connections = new Connections({ securityGroupRule: this }); constructor(private readonly cidrIp: string) { - this.connections = new SecurityGrouplessConnections(this); } /** @@ -49,7 +48,7 @@ export class CidrIp implements IConnectionPeer, IConnectable { /** * Any IPv4 address */ -export class AnyIPv4 extends CidrIp { +export class AnyIPv4 extends CidrIPv4 { constructor() { super("0.0.0.0/0"); } @@ -58,12 +57,11 @@ export class AnyIPv4 extends CidrIp { /** * A connection to a from a given IPv6 range */ -export class CidrIpv6 implements IConnectionPeer, IConnectable { +export class CidrIPv6 implements ISecurityGroupRule, IConnectable { public readonly canInlineRule = true; - public readonly connections: Connections; + public readonly connections: Connections = new Connections({ securityGroupRule: this }); constructor(private readonly cidrIpv6: string) { - this.connections = new SecurityGrouplessConnections(this); } /** @@ -83,7 +81,7 @@ export class CidrIpv6 implements IConnectionPeer, IConnectable { /** * Any IPv6 address */ -export class AnyIPv6 extends CidrIpv6 { +export class AnyIPv6 extends CidrIPv6 { constructor() { super("::0/0"); } @@ -98,12 +96,11 @@ export class AnyIPv6 extends CidrIpv6 { * * https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/vpc-endpoints.html */ -export class PrefixList implements IConnectionPeer, IConnectable { +export class PrefixList implements ISecurityGroupRule, IConnectable { public readonly canInlineRule = true; - public readonly connections: Connections; + public readonly connections: Connections = new Connections({ securityGroupRule: this }); constructor(private readonly prefixListId: string) { - this.connections = new SecurityGrouplessConnections(this); } public toIngressRuleJSON(): any { @@ -119,6 +116,9 @@ export class PrefixList implements IConnectionPeer, IConnectable { * Interface for classes that provide the connection-specification parts of a security group rule */ export interface IPortRange { + /** + * Whether the rule containing this port range can be inlined into a securitygroup or not. + */ readonly canInlineRule: boolean; /** @@ -220,4 +220,4 @@ export class AllConnections implements IPortRange { toPort: -1, }; } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2/lib/security-group.ts b/packages/@aws-cdk/aws-ec2/lib/security-group.ts index 4d4f67dccd581..b506df17aae46 100644 --- a/packages/@aws-cdk/aws-ec2/lib/security-group.ts +++ b/packages/@aws-cdk/aws-ec2/lib/security-group.ts @@ -1,6 +1,7 @@ -import { Construct, Token } from '@aws-cdk/cdk'; -import { IConnectionPeer, IPortRange } from './connection'; +import { Construct, Output, Token } from '@aws-cdk/cdk'; +import { Connections, IConnectable } from './connections'; import { cloudformation, SecurityGroupId, SecurityGroupVpcId } from './ec2.generated'; +import { IPortRange, ISecurityGroupRule } from './security-group-rule'; import { slugify } from './util'; import { VpcNetworkRef } from './vpc-ref'; @@ -11,30 +12,27 @@ export interface SecurityGroupRefProps { securityGroupId: SecurityGroupId; } -/** - * Basic interface for security groups - */ -export interface ISecurityGroup extends IConnectionPeer { - readonly securityGroupId: SecurityGroupId; - - addIngressRule(peer: IConnectionPeer, connection: IPortRange, description: string): void; - addEgressRule(peer: IConnectionPeer, connection: IPortRange, description: string): void; -} - /** * A SecurityGroup that is not created in this template */ -export class SecurityGroupRef extends Construct implements ISecurityGroup { - public readonly securityGroupId: SecurityGroupId; - public readonly canInlineRule = false; +export abstract class SecurityGroupRef extends Construct implements ISecurityGroupRule, IConnectable { + /** + * Import an existing SecurityGroup + */ + public static import(parent: Construct, id: string, props: SecurityGroupRefProps): SecurityGroupRef { + return new ImportedSecurityGroup(parent, id, props); + } - constructor(parent: Construct, name: string, props: SecurityGroupRefProps) { - super(parent, name); + public abstract readonly securityGroupId: SecurityGroupId; + public readonly canInlineRule = false; + public readonly connections = new Connections({ securityGroup: this }); - this.securityGroupId = props.securityGroupId; - } + /** + * FIXME: Where to place this?? + */ + public readonly defaultPortRange?: IPortRange; - public addIngressRule(peer: IConnectionPeer, connection: IPortRange, description: string) { + public addIngressRule(peer: ISecurityGroupRule, connection: IPortRange, description: string) { new cloudformation.SecurityGroupIngressResource(this, slugify(description), { groupId: this.securityGroupId, ...peer.toIngressRuleJSON(), @@ -43,7 +41,7 @@ export class SecurityGroupRef extends Construct implements ISecurityGroup { }); } - public addEgressRule(peer: IConnectionPeer, connection: IPortRange, description: string) { + public addEgressRule(peer: ISecurityGroupRule, connection: IPortRange, description: string) { new cloudformation.SecurityGroupEgressResource(this, slugify(description), { groupId: this.securityGroupId, ...peer.toEgressRuleJSON(), @@ -59,6 +57,16 @@ export class SecurityGroupRef extends Construct implements ISecurityGroup { public toEgressRuleJSON(): any { return { destinationSecurityGroupId: this.securityGroupId }; } + + /** + * Export this SecurityGroup for use in a different Stack + */ + public export(): SecurityGroupRefProps { + return { + securityGroupId: new Output(this, 'SecurityGroupId', { value: this.securityGroupId }).makeImportValue() + }; + } + } export interface SecurityGroupProps { @@ -105,15 +113,19 @@ export class SecurityGroup extends SecurityGroupRef { */ public readonly vpcId: SecurityGroupVpcId; + /** + * The ID of the security group + */ + public readonly securityGroupId: SecurityGroupId; + private readonly securityGroup: cloudformation.SecurityGroupResource; private readonly directIngressRules: cloudformation.SecurityGroupResource.IngressProperty[] = []; private readonly directEgressRules: cloudformation.SecurityGroupResource.EgressProperty[] = []; constructor(parent: Construct, name: string, props: SecurityGroupProps) { - super(parent, name, { securityGroupId: new Token(() => this.securityGroup.securityGroupId) }); + super(parent, name); const groupDescription = props.description || this.path; - this.securityGroup = new cloudformation.SecurityGroupResource(this, 'Resource', { groupName: props.groupName, groupDescription, @@ -122,11 +134,12 @@ export class SecurityGroup extends SecurityGroupRef { vpcId: props.vpc.vpcId, }); + this.securityGroupId = this.securityGroup.securityGroupId; this.groupName = this.securityGroup.ref; this.vpcId = this.securityGroup.securityGroupVpcId; } - public addIngressRule(peer: IConnectionPeer, connection: IPortRange, description: string) { + public addIngressRule(peer: ISecurityGroupRule, connection: IPortRange, description: string) { if (!peer.canInlineRule || !connection.canInlineRule) { super.addIngressRule(peer, connection, description); return; @@ -139,7 +152,7 @@ export class SecurityGroup extends SecurityGroupRef { }); } - public addEgressRule(peer: IConnectionPeer, connection: IPortRange, description: string) { + public addEgressRule(peer: ISecurityGroupRule, connection: IPortRange, description: string) { if (!peer.canInlineRule || !connection.canInlineRule) { super.addEgressRule(peer, connection, description); return; @@ -228,6 +241,19 @@ export interface ConnectionRule { description?: string; } +/** + * A SecurityGroup that hasn't been created here + */ +class ImportedSecurityGroup extends SecurityGroupRef { + public readonly securityGroupId: SecurityGroupId; + + constructor(parent: Construct, name: string, props: SecurityGroupRefProps) { + super(parent, name); + + this.securityGroupId = props.securityGroupId; + } +} + /** * Compare two ingress rules for equality the same way CloudFormation would (discarding description) */ diff --git a/packages/@aws-cdk/aws-ec2/test/test.connections.ts b/packages/@aws-cdk/aws-ec2/test/test.connections.ts index 50e9d82729a2d..5f9e3d0e36045 100644 --- a/packages/@aws-cdk/aws-ec2/test/test.connections.ts +++ b/packages/@aws-cdk/aws-ec2/test/test.connections.ts @@ -1,6 +1,8 @@ +import { expect, haveResource } from '@aws-cdk/assert'; import { Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; -import { Connections, IConnectable, SecurityGroup, TcpPort, VpcNetwork } from '../lib'; +import { AmazonLinuxImage, AutoScalingGroup, Connections, IConnectable, InstanceType, SecurityGroup, + SecurityGroupId, SecurityGroupRef, TcpAllPorts, TcpPort, VpcNetwork } from '../lib'; export = { 'peering between two security groups does not recursive infinitely'(test: Test) { @@ -11,13 +13,51 @@ export = { const sg1 = new SecurityGroup(stack, 'SG1', { vpc }); const sg2 = new SecurityGroup(stack, 'SG2', { vpc }); - const conn1 = new ConnectionsHolder(new Connections(sg1)); - const conn2 = new ConnectionsHolder(new Connections(sg2)); + const conn1 = new ConnectionsHolder(new Connections({ securityGroup: sg1 })); + const conn2 = new ConnectionsHolder(new Connections({ securityGroup: sg2 })); // WHEN conn1.connections.allowTo(conn2, new TcpPort(80), 'Test'); // THEN + test.done(); + }, + + '(imported) SecurityGroup can be used as target of .allowTo()'(test: Test) { + // GIVEN + const stack = new Stack(); + const vpc = new VpcNetwork(stack, 'VPC'); + const asg = new AutoScalingGroup(stack, 'ASG', { + instanceType: new InstanceType('t-1000'), + machineImage: new AmazonLinuxImage(), + vpc + }); + + const securityGroup = SecurityGroupRef.import(stack, 'ImportedSG', { securityGroupId: new SecurityGroupId('sg-12345') }); + + // WHEN + asg.connections.allowTo(securityGroup, new TcpAllPorts(), 'Connect there'); + + // THEN: rule to generated security group to connect to imported + expect(stack).to(haveResource("AWS::EC2::SecurityGroupEgress", { + GroupId: { "Fn::GetAtt": [ "ASGInstanceSecurityGroup0525485D", "GroupId" ] }, + IpProtocol: "tcp", + Description: "Connect there", + DestinationSecurityGroupId: "sg-12345", + FromPort: 0, + ToPort: 65535 + })); + + // THEN: rule to imported security group to allow connections from generated + expect(stack).to(haveResource("AWS::EC2::SecurityGroupIngress", { + IpProtocol: "tcp", + Description: "Connect there", + FromPort: 0, + GroupId: "sg-12345", + SourceSecurityGroupId: { "Fn::GetAtt": [ "ASGInstanceSecurityGroup0525485D", "GroupId" ] }, + ToPort: 65535 + })); + test.done(); } }; diff --git a/packages/@aws-cdk/aws-quickstarts/lib/database.ts b/packages/@aws-cdk/aws-quickstarts/lib/database.ts index 96c4f911afa6b..f57d135d9ef1f 100644 --- a/packages/@aws-cdk/aws-quickstarts/lib/database.ts +++ b/packages/@aws-cdk/aws-quickstarts/lib/database.ts @@ -20,13 +20,11 @@ export interface SqlServerProps { export class SqlServer extends cdk.Construct implements ec2.IConnectable { private static readonly PORT = 1433; public readonly connections: ec2.Connections; - public readonly defaultPortRange: ec2.IPortRange; - private readonly securityGroup: ec2.SecurityGroup; constructor(parent: cdk.Construct, name: string, props: SqlServerProps) { super(parent, name); - this.securityGroup = new ec2.SecurityGroup(this, 'SecurityGroup', { + const securityGroup = new ec2.SecurityGroup(this, 'SecurityGroup', { vpc: props.vpc, description: 'Database security group', }); @@ -54,10 +52,10 @@ export class SqlServer extends cdk.Construct implements ec2.IConnectable { masterUserPassword: p.masterPassword, port: SqlServer.PORT.toString(), dbSubnetGroupName: subnetGroup.ref, - vpcSecurityGroups: [ this.securityGroup.securityGroupId ] + vpcSecurityGroups: [ securityGroup.securityGroupId ] }); - this.defaultPortRange = new ec2.TcpPort(SqlServer.PORT); - this.connections = new ec2.Connections(this.securityGroup, this.defaultPortRange); + const defaultPortRange = new ec2.TcpPort(SqlServer.PORT); + this.connections = new ec2.Connections({ securityGroup, defaultPortRange }); } } diff --git a/packages/@aws-cdk/aws-quickstarts/lib/rdgw.ts b/packages/@aws-cdk/aws-quickstarts/lib/rdgw.ts index 004feca506547..d7bca3737dbe1 100644 --- a/packages/@aws-cdk/aws-quickstarts/lib/rdgw.ts +++ b/packages/@aws-cdk/aws-quickstarts/lib/rdgw.ts @@ -20,10 +20,9 @@ export interface RemoteDesktopGatewayProps { /** * Embed the Remote Desktop Gateway AWS QuickStart */ -export class RemoteDesktopGateway extends cdk.Construct implements ec2.IDefaultConnectable { +export class RemoteDesktopGateway extends cdk.Construct implements ec2.IConnectable { private static readonly PORT = 3389; public readonly connections: ec2.Connections; - public readonly defaultPortRange: ec2.IPortRange; constructor(parent: cdk.Construct, name: string, props: RemoteDesktopGatewayProps) { super(parent, name); @@ -48,11 +47,11 @@ export class RemoteDesktopGateway extends cdk.Construct implements ec2.IDefaultC parameters: params }); - const securityGroup = new ec2.SecurityGroupRef(this, 'SecurityGroup', { + const securityGroup = ec2.SecurityGroupRef.import(this, 'SecurityGroup', { securityGroupId: nestedStack.getAtt('Outputs.RemoteDesktopGatewaySGID') }); - this.defaultPortRange = new ec2.TcpPort(RemoteDesktopGateway.PORT); - this.connections = new ec2.Connections(securityGroup, this.defaultPortRange); + const defaultPortRange = new ec2.TcpPort(RemoteDesktopGateway.PORT); + this.connections = new ec2.Connections({ securityGroup, defaultPortRange }); } } diff --git a/packages/@aws-cdk/aws-rds/lib/cluster-ref.ts b/packages/@aws-cdk/aws-rds/lib/cluster-ref.ts index 747afffba2776..3ad5fe24036cb 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster-ref.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster-ref.ts @@ -5,7 +5,7 @@ import { DBClusterEndpointAddress } from './rds.generated'; /** * Create a clustered database with a given number of instances. */ -export abstract class DatabaseClusterRef extends cdk.Construct implements ec2.IDefaultConnectable { +export abstract class DatabaseClusterRef extends cdk.Construct implements ec2.IConnectable { /** * Import an existing DatabaseCluster from properties */ @@ -13,11 +13,6 @@ export abstract class DatabaseClusterRef extends cdk.Construct implements ec2.ID return new ImportedDatabaseCluster(parent, name, props); } - /** - * Default port to connect to this database - */ - public abstract readonly defaultPortRange: ec2.IPortRange; - /** * Access to the network connections */ @@ -159,7 +154,10 @@ class ImportedDatabaseCluster extends DatabaseClusterRef { this.securityGroupId = props.securityGroupId; this.defaultPortRange = new ec2.TcpPortFromAttribute(props.port); - this.connections = new ec2.Connections(new ec2.SecurityGroupRef(this, 'SecurityGroup', props), this.defaultPortRange); + this.connections = new ec2.Connections({ + securityGroup: ec2.SecurityGroupRef.import(this, 'SecurityGroup', props), + defaultPortRange: this.defaultPortRange + }); this.clusterIdentifier = props.clusterIdentifier; this.clusterEndpoint = new Endpoint(props.clusterEndpointAddress, props.port); this.readerEndpoint = new Endpoint(props.readerEndpointAddress, props.port); diff --git a/packages/@aws-cdk/aws-rds/lib/cluster.ts b/packages/@aws-cdk/aws-rds/lib/cluster.ts index 80ef230dbd255..c85c3238df141 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster.ts @@ -117,11 +117,6 @@ export class DatabaseCluster extends DatabaseClusterRef { */ public readonly instanceEndpoints: Endpoint[] = []; - /** - * Default port to connect to this database - */ - public readonly defaultPortRange: ec2.IPortRange; - /** * Access to the network connections */ @@ -214,8 +209,8 @@ export class DatabaseCluster extends DatabaseClusterRef { this.instanceEndpoints.push(new Endpoint(instance.dbInstanceEndpointAddress, instance.dbInstanceEndpointPort)); } - this.defaultPortRange = new ec2.TcpPortFromAttribute(this.clusterEndpoint.port); - this.connections = new ec2.Connections(securityGroup, this.defaultPortRange); + const defaultPortRange = new ec2.TcpPortFromAttribute(this.clusterEndpoint.port); + this.connections = new ec2.Connections({ securityGroup, defaultPortRange }); } } From 5164365e8101f4275b13cafddf8f0487f8dee148 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Thu, 16 Aug 2018 18:17:54 +0300 Subject: [PATCH 29/33] fix(s3): bucket notification dependencies (#584) Allow bucket notification destinations to specify dependency elements that are added to the bucket notification resource. This is to ensure that S3 will be able to validate the subscription when notifications are configured. --- .../@aws-cdk/aws-lambda/lib/lambda-ref.ts | 7 +++- .../integ.bucket-notifications.expected.json | 10 ++++-- .../aws-s3-notifications/lib/destination.ts | 10 ++++-- .../notifications-resource.ts | 36 +++++++++++-------- .../aws-s3/test/test.notifications.ts | 29 +++++++++++++++ packages/@aws-cdk/aws-sns/lib/policy.ts | 13 +++++-- packages/@aws-cdk/aws-sns/lib/topic-ref.ts | 3 +- ...teg.sns-bucket-notifications.expected.json | 6 +++- packages/@aws-cdk/aws-sns/test/test.sns.ts | 3 ++ packages/@aws-cdk/aws-sqs/lib/policy.ts | 13 +++++-- packages/@aws-cdk/aws-sqs/lib/queue-ref.ts | 3 +- .../integ.bucket-notifications.expected.json | 11 ++++-- packages/@aws-cdk/aws-sqs/test/test.sqs.ts | 4 +++ 13 files changed, 117 insertions(+), 31 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts b/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts index 9bf961f808fbf..505562b050785 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts @@ -278,9 +278,14 @@ export abstract class FunctionRef extends cdk.Construct }); } + // if we have a permission resource for this relationship, add it as a dependency + // to the bucket notifications resource, so it will be created first. + const permission = this.tryFindChild(permissionId) as cdk.Resource; + return { type: s3n.BucketNotificationDestinationType.Lambda, - arn: this.functionArn + arn: this.functionArn, + dependencies: permission ? [ permission ] : undefined }; } diff --git a/packages/@aws-cdk/aws-lambda/test/integ.bucket-notifications.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.bucket-notifications.expected.json index edbf24da3f3f2..67b30b027eb0f 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.bucket-notifications.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.bucket-notifications.expected.json @@ -40,7 +40,10 @@ } ] } - } + }, + "DependsOn": [ + "MyFunctionAllowBucketNotificationsFromlambdabucketnotificationsMyBucket0F0FC402189522F6" + ] }, "MyFunctionServiceRole3C357FF2": { "Type": "AWS::IAM::Role", @@ -170,7 +173,10 @@ } ] } - } + }, + "DependsOn": [ + "MyFunctionAllowBucketNotificationsFromlambdabucketnotificationsYourBucket307F72F245F2C5AE" + ] }, "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC": { "Type": "AWS::IAM::Role", diff --git a/packages/@aws-cdk/aws-s3-notifications/lib/destination.ts b/packages/@aws-cdk/aws-s3-notifications/lib/destination.ts index d48960a02c82d..286aa4a80e56d 100644 --- a/packages/@aws-cdk/aws-s3-notifications/lib/destination.ts +++ b/packages/@aws-cdk/aws-s3-notifications/lib/destination.ts @@ -22,12 +22,18 @@ export interface BucketNotificationDestinationProps { /** * The notification type. */ - readonly type: BucketNotificationDestinationType; + type: BucketNotificationDestinationType; /** * The ARN of the destination (i.e. Lambda, SNS, SQS). */ - readonly arn: cdk.Arn; + arn: cdk.Arn; + + /** + * Any additional dependencies that should be resolved before the bucket notification + * can be configured (for example, the SNS Topic Policy resource). + */ + dependencies?: cdk.IDependable[] } /** diff --git a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts index 3dec683c961a5..fd26e13a71044 100644 --- a/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts +++ b/packages/@aws-cdk/aws-s3/lib/notifications-resource/notifications-resource.ts @@ -32,7 +32,7 @@ export class BucketNotifications extends cdk.Construct { private readonly lambdaNotifications = new Array(); private readonly queueNotifications = new Array(); private readonly topicNotifications = new Array(); - private customResourceCreated = false; + private resource?: cdk.Resource; private readonly bucket: Bucket; constructor(parent: cdk.Construct, id: string, props: NotificationsProps) { @@ -49,7 +49,7 @@ export class BucketNotifications extends cdk.Construct { * @param filters A set of S3 key filters */ public addNotification(event: EventType, target: IBucketNotificationDestination, ...filters: NotificationKeyFilter[]) { - this.createResourceOnce(); + const resource = this.createResourceOnce(); // resolve target. this also provides an opportunity for the target to e.g. update // policies to allow this notification to happen. @@ -59,6 +59,13 @@ export class BucketNotifications extends cdk.Construct { Filter: renderFilters(filters), }; + // if the target specifies any dependencies, add them to the custom resource. + // for example, the SNS topic policy must be created /before/ the notification resource. + // otherwise, S3 won't be able to confirm the subscription. + if (targetProps.dependencies) { + resource.addDependency(...targetProps.dependencies); + } + // based on the target type, add the the correct configurations array switch (targetProps.type) { case BucketNotificationDestinationType.Lambda: @@ -92,21 +99,20 @@ export class BucketNotifications extends cdk.Construct { * there is no notifications resource. */ private createResourceOnce() { - if (this.customResourceCreated) { - return; + if (!this.resource) { + const handlerArn = NotificationsResourceHandler.singleton(this); + + this.resource = new cdk.Resource(this, 'Resource', { + type: 'Custom::S3BucketNotifications', + properties: { + ServiceToken: handlerArn, + BucketName: this.bucket.bucketName, + NotificationConfiguration: new cdk.Token(() => this.renderNotificationConfiguration()) + } + }); } - const handlerArn = NotificationsResourceHandler.singleton(this); - new cdk.Resource(this, 'Resource', { - type: 'Custom::S3BucketNotifications', - properties: { - ServiceToken: handlerArn, - BucketName: this.bucket.bucketName, - NotificationConfiguration: new cdk.Token(() => this.renderNotificationConfiguration()) - } - }); - - this.customResourceCreated = true; + return this.resource; } } diff --git a/packages/@aws-cdk/aws-s3/test/test.notifications.ts b/packages/@aws-cdk/aws-s3/test/test.notifications.ts index d8e210fe4fa30..f229f9d307bcd 100644 --- a/packages/@aws-cdk/aws-s3/test/test.notifications.ts +++ b/packages/@aws-cdk/aws-s3/test/test.notifications.ts @@ -1,6 +1,7 @@ import { expect, haveResource } from '@aws-cdk/assert'; import s3n = require('@aws-cdk/aws-s3-notifications'); import cdk = require('@aws-cdk/cdk'); +import { Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import s3 = require('../lib'); import { Topic } from './notification-dests'; @@ -268,6 +269,34 @@ export = { } })); + test.done(); + }, + + 'a notification destination can specify a set of dependencies that must be resolved before the notifications resource is created'(test: Test) { + const stack = new Stack(); + + const bucket = new s3.Bucket(stack, 'Bucket'); + const dependent = new cdk.Resource(stack, 'Dependent', { type: 'DependOnMe' }); + const dest: s3n.IBucketNotificationDestination = { + asBucketNotificationDestination: () => ({ + arn: new cdk.Arn('arn'), + type: s3n.BucketNotificationDestinationType.Queue, + dependencies: [ dependent ] + }) + }; + + bucket.onObjectCreated(dest); + + test.deepEqual(stack.toCloudFormation().Resources.BucketNotifications8F2E257D, { + Type: 'Custom::S3BucketNotifications', + Properties: { + ServiceToken: { 'Fn::GetAtt': [ 'BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691', 'Arn' ] }, + BucketName: { Ref: 'Bucket83908E77' }, + NotificationConfiguration: { QueueConfigurations: [ { Events: [ 's3:ObjectCreated:*' ], QueueArn: 'arn' } ] } + }, + DependsOn: [ 'Dependent' ] + }); + test.done(); } }; diff --git a/packages/@aws-cdk/aws-sns/lib/policy.ts b/packages/@aws-cdk/aws-sns/lib/policy.ts index ac14d700e867f..4a22ee7a2975b 100644 --- a/packages/@aws-cdk/aws-sns/lib/policy.ts +++ b/packages/@aws-cdk/aws-sns/lib/policy.ts @@ -1,4 +1,4 @@ -import { Construct, PolicyDocument } from '@aws-cdk/cdk'; +import { Construct, IDependable, PolicyDocument } from '@aws-cdk/cdk'; import { cloudformation } from './sns.generated'; import { TopicRef } from './topic-ref'; @@ -12,18 +12,25 @@ export interface TopicPolicyProps { /** * Applies a policy to SNS topics. */ -export class TopicPolicy extends Construct { +export class TopicPolicy extends Construct implements IDependable { /** * The IAM policy document for this policy. */ public readonly document = new PolicyDocument(); + /** + * Allows topic policy to be added as a dependency. + */ + public readonly dependencyElements = new Array(); + constructor(parent: Construct, name: string, props: TopicPolicyProps) { super(parent, name); - new cloudformation.TopicPolicyResource(this, 'Resource', { + const resource = new cloudformation.TopicPolicyResource(this, 'Resource', { policyDocument: this.document, topics: props.topics.map(t => t.topicArn) }); + + this.dependencyElements.push(resource); } } diff --git a/packages/@aws-cdk/aws-sns/lib/topic-ref.ts b/packages/@aws-cdk/aws-sns/lib/topic-ref.ts index 563915b5cc65c..ec16ea348d5a3 100644 --- a/packages/@aws-cdk/aws-sns/lib/topic-ref.ts +++ b/packages/@aws-cdk/aws-sns/lib/topic-ref.ts @@ -302,7 +302,8 @@ export abstract class TopicRef extends cdk.Construct implements events.IEventRul return { arn: this.topicArn, - type: s3n.BucketNotificationDestinationType.Topic + type: s3n.BucketNotificationDestinationType.Topic, + dependencies: [ this.policy! ] // make sure the topic policy resource is created before the notification config }; } } diff --git a/packages/@aws-cdk/aws-sns/test/integ.sns-bucket-notifications.expected.json b/packages/@aws-cdk/aws-sns/test/integ.sns-bucket-notifications.expected.json index 011685cf9af87..f282f4ce68fad 100644 --- a/packages/@aws-cdk/aws-sns/test/integ.sns-bucket-notifications.expected.json +++ b/packages/@aws-cdk/aws-sns/test/integ.sns-bucket-notifications.expected.json @@ -127,7 +127,11 @@ } ] } - } + }, + "DependsOn": [ + "ObjectCreatedTopicPolicyA938ECFC", + "ObjectDeletedTopicPolicy026B02E6" + ] }, "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC": { "Type": "AWS::IAM::Role", diff --git a/packages/@aws-cdk/aws-sns/test/test.sns.ts b/packages/@aws-cdk/aws-sns/test/test.sns.ts index 1b44251efb67c..e30672b03fa53 100644 --- a/packages/@aws-cdk/aws-sns/test/test.sns.ts +++ b/packages/@aws-cdk/aws-sns/test/test.sns.ts @@ -703,6 +703,9 @@ export = { test.deepEqual(resolve(dest1.arn), resolve(topic.topicArn)); test.deepEqual(dest1.type, s3n.BucketNotificationDestinationType.Topic); + const dep: cdk.Construct = dest1.dependencies![0] as any; + test.deepEqual((dep.children[0] as any).logicalId, 'MyTopicPolicy12A5EC17', 'verify topic policy is added as dependency'); + // calling again on the same bucket yields is idempotent const dest2 = topic.asBucketNotificationDestination(bucketArn, bucketId); test.deepEqual(resolve(dest2.arn), resolve(topic.topicArn)); diff --git a/packages/@aws-cdk/aws-sqs/lib/policy.ts b/packages/@aws-cdk/aws-sqs/lib/policy.ts index 28c9cff03ecf9..22fd03c361872 100644 --- a/packages/@aws-cdk/aws-sqs/lib/policy.ts +++ b/packages/@aws-cdk/aws-sqs/lib/policy.ts @@ -1,4 +1,4 @@ -import { Construct, PolicyDocument } from '@aws-cdk/cdk'; +import { Construct, IDependable, PolicyDocument } from '@aws-cdk/cdk'; import { QueueRef } from './queue-ref'; import { cloudformation } from './sqs.generated'; @@ -12,18 +12,25 @@ export interface QueuePolicyProps { /** * Applies a policy to SQS queues. */ -export class QueuePolicy extends Construct { +export class QueuePolicy extends Construct implements IDependable { /** * The IAM policy document for this policy. */ public readonly document = new PolicyDocument(); + /** + * Allows adding QueuePolicy as a dependency. + */ + public readonly dependencyElements = new Array(); + constructor(parent: Construct, name: string, props: QueuePolicyProps) { super(parent, name); - new cloudformation.QueuePolicyResource(this, 'Resource', { + const resource = new cloudformation.QueuePolicyResource(this, 'Resource', { policyDocument: this.document, queues: props.queues.map(q => q.queueUrl) }); + + this.dependencyElements.push(resource); } } diff --git a/packages/@aws-cdk/aws-sqs/lib/queue-ref.ts b/packages/@aws-cdk/aws-sqs/lib/queue-ref.ts index df9493566fd3f..047abfa325222 100644 --- a/packages/@aws-cdk/aws-sqs/lib/queue-ref.ts +++ b/packages/@aws-cdk/aws-sqs/lib/queue-ref.ts @@ -106,7 +106,8 @@ export abstract class QueueRef extends cdk.Construct implements s3n.IBucketNotif return { arn: this.queueArn, - type: s3n.BucketNotificationDestinationType.Queue + type: s3n.BucketNotificationDestinationType.Queue, + dependencies: [ this.policy! ] }; } } diff --git a/packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.expected.json b/packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.expected.json index 93adc81f02f79..9bca049124ab9 100644 --- a/packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.expected.json +++ b/packages/@aws-cdk/aws-sqs/test/integ.bucket-notifications.expected.json @@ -41,7 +41,11 @@ } ] } - } + }, + "DependsOn": [ + "MyQueuePolicy6BBEDDAC", + "EncryptedQueuePolicy8AEB1708" + ] }, "MyQueueE6CA6235": { "Type": "AWS::SQS::Queue" @@ -227,7 +231,10 @@ } ] } - } + }, + "DependsOn": [ + "MyQueuePolicy6BBEDDAC" + ] }, "EncryptedQueueKey6F4FD304": { "Type": "AWS::KMS::Key", diff --git a/packages/@aws-cdk/aws-sqs/test/test.sqs.ts b/packages/@aws-cdk/aws-sqs/test/test.sqs.ts index 00aca29685afc..135d37d76a6d2 100644 --- a/packages/@aws-cdk/aws-sqs/test/test.sqs.ts +++ b/packages/@aws-cdk/aws-sqs/test/test.sqs.ts @@ -315,6 +315,10 @@ export = { } })); + // make sure the queue policy is added as a dependency to the bucket + // notifications resource so it will be created first. + test.deepEqual(stack.toCloudFormation().Resources.BucketNotifications8F2E257D.DependsOn, [ 'QueuePolicy25439813' ]); + test.done(); }, From 9ffb4c968ad0a7239df538f5e1d61eb93c87e124 Mon Sep 17 00:00:00 2001 From: James Cooke Date: Thu, 16 Aug 2018 18:17:03 +0100 Subject: [PATCH 30/33] feat(aws-rds): support aurora postgresql/mysql database cluster engines (#586) --- packages/@aws-cdk/aws-rds/lib/props.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/@aws-cdk/aws-rds/lib/props.ts b/packages/@aws-cdk/aws-rds/lib/props.ts index 9533f66608db1..3f1b52ddc12a1 100644 --- a/packages/@aws-cdk/aws-rds/lib/props.ts +++ b/packages/@aws-cdk/aws-rds/lib/props.ts @@ -6,6 +6,8 @@ import cdk = require('@aws-cdk/cdk'); */ export enum DatabaseClusterEngine { Aurora = 'aurora', + AuroraMysql = 'aurora-mysql', + AuroraPostgresql = 'aurora-postgresql', Neptune = 'neptune' } From b6a5e890e84d7c8547c114a94557898579786e18 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Thu, 16 Aug 2018 19:18:56 +0200 Subject: [PATCH 31/33] chore: remove file that was accidentally committed (#585) --- .../bin/simple-license-checker.js | 97 ------------------- 1 file changed, 97 deletions(-) delete mode 100644 packages/simple-license-checker/bin/simple-license-checker.js diff --git a/packages/simple-license-checker/bin/simple-license-checker.js b/packages/simple-license-checker/bin/simple-license-checker.js deleted file mode 100644 index 59060ccb88f9a..0000000000000 --- a/packages/simple-license-checker/bin/simple-license-checker.js +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env node -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -var path = require("path"); -require("source-map-support/register"); -var util = require("util"); -var yargs = require("yargs"); -// tslint:disable-next-line:no-var-requires -var nlv = require('node-license-validator'); -var argv = yargs - .option('allow-licenses', { type: 'array', alias: 'a', desc: 'Additional licenses to allow', default: [] }) - .option('allow-packages', { type: 'array', alias: 'a', desc: 'Packages to allow by default', default: [] }) - .argv; -/** - * Set of permissive licenses allowed by default - */ -var PERMISSIVE_LICENSES = [ - // MIT variants - 'MIT', 'ISC', 'MIT/X11', - // BSD variants - 'BSD', 'BSD-2-Clause', 'BSD-3-Clause', - // Public domain - 'CC0-1.0', - 'Unlicense', - // Creative Commons - 'CC-BY-3.0', - // Organizations - 'Apache-2.0', - 'Apache 2.0', - 'PSF', -]; -function main() { - return __awaiter(this, void 0, void 0, function () { - var settings, licenses, packages, results; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - settings = require(path.join(process.cwd(), 'package.json'))["simple-license-checker"] || {}; - licenses = PERMISSIVE_LICENSES.concat(argv['allow-licenses']).concat(settings["allow-licenses"] || []); - packages = argv['allow-packages'].concat(Object.keys(settings["allow-packages"] || {})); - return [4 /*yield*/, util.promisify(nlv)('.', { licenses: licenses, packages: packages })]; - case 1: - results = _a.sent(); - if (results.invalids.length > 0) { - process.stderr.write('Uses dependencies with nonpermissive (or unknown) licenses:\n'); - results.invalids.forEach(function (pkg) { - var license = results.packages[pkg]; - process.stderr.write("* " + pkg + " => " + license + "\n"); - }); - process.exit(1); - } - return [2 /*return*/]; - } - }); - }); -} -main().catch(function (err) { - // tslint:disable-next-line:no-console - console.error(err); - process.exit(1); -}); -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2ltcGxlLWxpY2Vuc2UtY2hlY2tlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInNpbXBsZS1saWNlbnNlLWNoZWNrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFDQSwyQkFBOEI7QUFDOUIsdUNBQXFDO0FBQ3JDLDJCQUE4QjtBQUM5Qiw2QkFBZ0M7QUFFaEMsMkNBQTJDO0FBQzNDLElBQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO0FBUTlDLElBQU0sSUFBSSxHQUFHLEtBQUs7S0FDYixNQUFNLENBQUMsZ0JBQWdCLEVBQUUsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLDhCQUE4QixFQUFFLE9BQU8sRUFBRSxFQUFFLEVBQUUsQ0FBQztLQUMxRyxNQUFNLENBQUMsZ0JBQWdCLEVBQUUsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLDhCQUE4QixFQUFFLE9BQU8sRUFBRSxFQUFFLEVBQUUsQ0FBQztLQUMxRyxJQUFJLENBQUM7QUFFVjs7R0FFRztBQUNILElBQU0sbUJBQW1CLEdBQUc7SUFDeEIsZUFBZTtJQUNmLEtBQUssRUFBRSxLQUFLLEVBQUUsU0FBUztJQUN2QixlQUFlO0lBQ2YsS0FBSyxFQUFFLGNBQWMsRUFBRSxjQUFjO0lBQ3JDLGdCQUFnQjtJQUNoQixTQUFTO0lBQ1QsV0FBVztJQUNYLG1CQUFtQjtJQUNuQixXQUFXO0lBQ1gsZ0JBQWdCO0lBQ2hCLFlBQVk7SUFDWixZQUFZO0lBQ1osS0FBSztDQUNSLENBQUM7QUFFRjs7Ozs7O29CQUNVLFFBQVEsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEVBQUUsY0FBYyxDQUFDLENBQUMsQ0FBQyx3QkFBd0IsQ0FBQyxJQUFJLEVBQUUsQ0FBQztvQkFFN0YsUUFBUSxHQUFHLG1CQUFtQixDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztvQkFDdkcsUUFBUSxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUM7b0JBRWxFLHFCQUFNLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUUsUUFBUSxVQUFBLEVBQUUsUUFBUSxVQUFBLEVBQUUsQ0FBQyxFQUFBOztvQkFBNUUsT0FBTyxHQUFlLFNBQXNEO29CQUVsRixJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTt3QkFDN0IsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsK0RBQStELENBQUMsQ0FBQzt3QkFDdEYsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsVUFBQSxHQUFHOzRCQUN4QixJQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDOzRCQUN0QyxPQUFPLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxPQUFLLEdBQUcsWUFBTyxPQUFPLE9BQUksQ0FBQyxDQUFDO3dCQUNyRCxDQUFDLENBQUMsQ0FBQzt3QkFFSCxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO3FCQUNuQjs7Ozs7Q0FDSjtBQUVELElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxVQUFBLEdBQUc7SUFDWixzQ0FBc0M7SUFDdEMsT0FBTyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUNuQixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ3BCLENBQUMsQ0FBQyxDQUFDIn0= \ No newline at end of file From d8895790c21af7554f2013c15f280651cef94636 Mon Sep 17 00:00:00 2001 From: Doug-AWS Date: Thu, 9 Aug 2018 06:52:57 -0700 Subject: [PATCH 32/33] Updated blurb about installing via npm to note it installs the latest released version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 926340b160ff4..85d012a7f8427 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ on AWS. * For a detailed walkthrough, see [Getting Started] in the AWS CDK [User Guide] * See [Manual Installation](./MANUAL_INSTALLATION.md) for installing the CDK from a signed .zip file -Install the [AWS CDK Toolkit] from npm (requires [Node.js ≥ 8.11.x](https://nodejs.org/en/download)): +Install the latest released version of the [AWS CDK Toolkit] from npm (requires [Node.js ≥ 8.11.x](https://nodejs.org/en/download)): ```bash $ npm i -g aws-cdk From 89935c41db5cf14cf2a81970fa6e7dabd59bdbfa Mon Sep 17 00:00:00 2001 From: Doug Date: Mon, 13 Aug 2018 10:14:55 -0700 Subject: [PATCH 33/33] Update README.md Explicitly called out upgrading --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 85d012a7f8427..28c0f8933ccb8 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ on AWS. * For a detailed walkthrough, see [Getting Started] in the AWS CDK [User Guide] * See [Manual Installation](./MANUAL_INSTALLATION.md) for installing the CDK from a signed .zip file -Install the latest released version of the [AWS CDK Toolkit] from npm (requires [Node.js ≥ 8.11.x](https://nodejs.org/en/download)): +Install the [AWS CDK Toolkit] or update your installed version of the |cdk| to the latest version from npm (requires [Node.js ≥ 8.11.x](https://nodejs.org/en/download)): ```bash $ npm i -g aws-cdk