Skip to content

Commit

Permalink
Merge branch 'master' into huijbers/markdownlint
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] committed Jan 31, 2022
2 parents 1291e9c + 4491014 commit 8f8dc48
Show file tree
Hide file tree
Showing 46 changed files with 812 additions and 391 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [1.142.0](https://github.com/aws/aws-cdk/compare/v1.141.0...v1.142.0) (2022-01-28)


### Features

* **cfnspec:** cloudformation spec v53.1.0 ([#18680](https://github.com/aws/aws-cdk/issues/18680)) ([f385059](https://github.com/aws/aws-cdk/commit/f38505911a3e140a9cb6b269bdf22abe9803c515))
* **cloudfront-origins:** extend `readTimeout` maximum value for `HttpOriginProps` ([#18697](https://github.com/aws/aws-cdk/issues/18697)) ([e64de67](https://github.com/aws/aws-cdk/commit/e64de677cdfc014f68e92b204f4728e60a8bb111)), closes [#18628](https://github.com/aws/aws-cdk/issues/18628)
* **eks:** cluster logging ([#18112](https://github.com/aws/aws-cdk/issues/18112)) ([872277b](https://github.com/aws/aws-cdk/commit/872277b9e853dbf5f2cac84b5afb6d26e0ed5659)), closes [#4159](https://github.com/aws/aws-cdk/issues/4159)
* **iotevents:** allow setting description, evaluation method and key of DetectorModel ([#18644](https://github.com/aws/aws-cdk/issues/18644)) ([2eeaebc](https://github.com/aws/aws-cdk/commit/2eeaebc3cdc9c5c7ef3fa312b3d1abca265dcbb6))
* **lambda-python:** support setting environment vars for bundling ([#18635](https://github.com/aws/aws-cdk/issues/18635)) ([30e2233](https://github.com/aws/aws-cdk/commit/30e223333fef0b0d7f12287dab170a34e092d7fa))


### Bug Fixes

* **aws-lambda-nodejs:** pre compilation with tsc is not being run ([#18062](https://github.com/aws/aws-cdk/issues/18062)) ([7ac7221](https://github.com/aws/aws-cdk/commit/7ac7221aff3c612ab80e7812c371b11c56e5db0a)), closes [#18002](https://github.com/aws/aws-cdk/issues/18002)
* **pipelines:** undeployable due to dependency cycle ([#18686](https://github.com/aws/aws-cdk/issues/18686)) ([009d689](https://github.com/aws/aws-cdk/commit/009d68912267de9dcf4136a7d80a652a891b7bb9)), closes [#18492](https://github.com/aws/aws-cdk/issues/18492) [#18673](https://github.com/aws/aws-cdk/issues/18673)

## [1.141.0](https://github.com/aws/aws-cdk/compare/v1.140.0...v1.141.0) (2022-01-27)


Expand Down
123 changes: 123 additions & 0 deletions packages/@aws-cdk/assertions/MIGRATING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Migrating to Assertions

Most of the APIs in the old `assert` module has a corresponding API in `assertions`.
Make the following modifications to your CDK test files to migrate to the
`@aws-cdk/assertions` module.

For a migration script that handles most common use cases for you, see
[Migration Script](migration-script).

## Translation Guide

- Rewrite module imports that use `@aws-cdk/aws-assert` to `@aws-cdk/aws-assertions`.
For example:

```ts
import '@aws-cdk/assert/jest';
import { ABSENT, SynthUtils, ResourcePart } from '@aws-cdk/assert';
```

...becomes...

```ts
import { Template } from '@aws-cdk/assertions';
import { Match, Template } from '@aws-cdk/assertions';
```

- Replace instances of `toHaveResource()` with `hasResourceProperties()` or `hasResource()`.
For example:

```ts
expect(stack).toHaveResource('FOO::BAR', {/*...*/});
expect(stack).toHaveResource('FOO::BAR', {/*...*/}, ResourcePart.CompleteDefinition);
```

...becomes...

```ts
Template.fromStack(stack).hasResourceProperties('FOO::BAR', {/*...*/});
Template.fromStack(stacK).hasResource('FOO::BAR', {/*...*/});
```

- Replace instances of `toCountResources()` with `resourceCountIs`. For example:

```ts
expect(stack).toCountResources('FOO::BAR', 1);
```

...becomes...

```ts
Template.fromStack(stack).resourceCountIs('FOO::BAR', 1);
```
- Replace instances of `toMatchTemplate()` with `templateMatches()`. For example:

```ts
expect(stack).toMatchTemplate({/*...*/});
```

...becomes...

```ts
Template.fromStack(stack).templateMatches({/*...*/});
```

- Replace `arrayWith()` with `Match.arrayWith()`, `objectLike()` with `Match.objectLike()`, and
`ABSENT` with `Match.absent()`.

- `not` can be replaced with `Match.not()` _or_ `resourceCountIs()` depending on the use case.

```ts
// asserting that the stack does not have a particular resource.
expect(stack).not.toHaveResource('FOO::BAR');
```

...becomes...

```ts
Template.fromStack(stack).resourceCountIs('FOO::BAR', 0);
```

```ts
// asserting that the stack does not have a resource with these properties
expect(stack).not.toHaveResource('FOO::BAR', {
prop: 'does not exist',
});
```

...becomes...

```ts
Template.fromStack(stack).hasResourceProperties('FOO::BAR', Match.not({
prop: 'does not exist',
}));
```

- `SynthUtils.synthesize(stack)` can be replaced as well. For example:

```ts
expect(SynthUtils.synthesize(stack).template).toEqual(/*...*/);
SynthUtils.syntesize(stack);
```

...becomes...

```ts
expect(Template.fromStack(stack).toJSON()).toEqual(/*...*/);
App.of(stack).synth();
```

## Migration Script

> NOTE: We have some code rewrite rules that will make it easier to migrate from one library
> to the other. This tool will not do a complete rewrite and is not guaranteed to produce
> compilable code! It will just save you the effort of performing a lot of code substitutions
> you would otherwise have to do by hand.

Comby is a tool used to do structured code rewriting. You can install it
[here](https://comby.dev/). Download the [rewrite.toml](rewrite.toml) file from our GitHub
repository, and run the following command in the root directory of your project:

```bash
comby -config ~/rewrite.toml -f .ts -d test -in-place -timeout 10
```
3 changes: 3 additions & 0 deletions packages/@aws-cdk/assertions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

<!--END STABILITY BANNER-->

If you're migrating from the old `assert` library, the migration guide can be found in
[our GitHub repository](https://github.com/aws/aws-cdk/blob/master/packages/@aws-cdk/assertions/MIGRATING.md).

Functions for writing test asserting against CDK applications, with focus on CloudFormation templates.

The `Template` class includes a set of methods for writing assertions against CloudFormation templates. Use one of the `Template.fromXxx()` static methods to create an instance of this class.
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/assertions/lib/private/conditions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { filterLogicalId, formatFailure, matchSection } from './section';
import { Template } from './template';

export function findConditions(template: Template, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } {
const section: { [key: string] : {} } = template.Conditions;
const section: { [key: string] : {} } = template.Conditions ?? {};
const result = matchSection(filterLogicalId(section, logicalId), props);

if (!result.match) {
Expand All @@ -13,7 +13,7 @@ export function findConditions(template: Template, logicalId: string, props: any
}

export function hasCondition(template: Template, logicalId: string, props: any): string | void {
const section: { [key: string] : {} } = template.Conditions;
const section: { [key: string] : {} } = template.Conditions ?? {};
const result = matchSection(filterLogicalId(section, logicalId), props);
if (result.match) {
return;
Expand Down
175 changes: 175 additions & 0 deletions packages/@aws-cdk/assertions/lib/private/cyclic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { Resource, Template } from './template';

/**
* Check a template for cyclic dependencies
*
* This will make sure that we don't happily validate templates
* in unit tests that wouldn't deploy to CloudFormation anyway.
*/
export function checkTemplateForCyclicDependencies(template: Template): void {
const logicalIds = new Set(Object.keys(template.Resources ?? {}));

const dependencies = new Map<string, Set<string>>();
for (const [logicalId, resource] of Object.entries(template.Resources ?? {})) {
dependencies.set(logicalId, intersect(findResourceDependencies(resource), logicalIds));
}

// We will now progressively remove entries from the map of 'dependencies' that have
// 0 elements in them. If we can't do that anymore and the map isn't empty, we
// have a cyclic dependency.
while (dependencies.size > 0) {
const free = Array.from(dependencies.entries()).filter(([_, deps]) => deps.size === 0);
if (free.length === 0) {
// Oops!
const cycle = findCycle(dependencies);

const cycleResources: any = {};
for (const logicalId of cycle) {
cycleResources[logicalId] = template.Resources?.[logicalId];
}

throw new Error(`Template is undeployable, these resources have a dependency cycle: ${cycle.join(' -> ')}:\n\n${JSON.stringify(cycleResources, undefined, 2)}`);
}

for (const [logicalId, _] of free) {
for (const deps of dependencies.values()) {
deps.delete(logicalId);
}
dependencies.delete(logicalId);
}
}
}

function findResourceDependencies(res: Resource): Set<string> {
return new Set([
...toArray(res.DependsOn ?? []),
...findExpressionDependencies(res.Properties),
]);
}

function toArray<A>(x: A | A[]): A[] {
return Array.isArray(x) ? x : [x];
}

function findExpressionDependencies(obj: any): Set<string> {
const ret = new Set<string>();
recurse(obj);
return ret;

function recurse(x: any): void {
if (!x) { return; }
if (Array.isArray(x)) {
x.forEach(recurse);
}
if (typeof x === 'object') {
const keys = Object.keys(x);
if (keys.length === 1 && keys[0] === 'Ref') {
ret.add(x[keys[0]]);
} else if (keys.length === 1 && keys[0] === 'Fn::GetAtt') {
ret.add(x[keys[0]][0]);
} else if (keys.length === 1 && keys[0] === 'Fn::Sub') {
const argument = x[keys[0]];
const pattern = Array.isArray(argument) ? argument[0] : argument;
for (const logId of logicalIdsInSubString(pattern)) {
ret.add(logId);
}
const contextDict = Array.isArray(argument) ? argument[1] : undefined;
if (contextDict) {
Object.values(contextDict).forEach(recurse);
}
} else {
Object.values(x).forEach(recurse);
}
}
}
}

/**
* Return the logical IDs found in a {Fn::Sub} format string
*/
function logicalIdsInSubString(x: string): string[] {
return analyzeSubPattern(x).flatMap((fragment) => {
switch (fragment.type) {
case 'getatt':
case 'ref':
return [fragment.logicalId];
case 'literal':
return [];
}
});
}


function analyzeSubPattern(pattern: string): SubFragment[] {
const ret: SubFragment[] = [];
let start = 0;

let ph0 = pattern.indexOf('${', start);
while (ph0 > -1) {
if (pattern[ph0 + 2] === '!') {
// "${!" means "don't actually substitute"
start = ph0 + 3;
ph0 = pattern.indexOf('${', start);
continue;
}

const ph1 = pattern.indexOf('}', ph0 + 2);
if (ph1 === -1) {
break;
}
const placeholder = pattern.substring(ph0 + 2, ph1);

if (ph0 > start) {
ret.push({ type: 'literal', content: pattern.substring(start, ph0) });
}
if (placeholder.includes('.')) {
const [logicalId, attr] = placeholder.split('.');
ret.push({ type: 'getatt', logicalId: logicalId!, attr: attr! });
} else {
ret.push({ type: 'ref', logicalId: placeholder });
}

start = ph1 + 1;
ph0 = pattern.indexOf('${', start);
}

if (start < pattern.length - 1) {
ret.push({ type: 'literal', content: pattern.substr(start) });
}

return ret;
}

type SubFragment =
| { readonly type: 'literal'; readonly content: string }
| { readonly type: 'ref'; readonly logicalId: string }
| { readonly type: 'getatt'; readonly logicalId: string; readonly attr: string };


function intersect<A>(xs: Set<A>, ys: Set<A>): Set<A> {
return new Set<A>(Array.from(xs).filter(x => ys.has(x)));
}

/**
* Find cycles in a graph
*
* Not the fastest, but effective and should be rare
*/
function findCycle(deps: ReadonlyMap<string, ReadonlySet<string>>): string[] {
for (const node of deps.keys()) {
const cycle = recurse(node, [node]);
if (cycle) { return cycle; }
}
throw new Error('No cycle found. Assertion failure!');

function recurse(node: string, path: string[]): string[] | undefined {
for (const dep of deps.get(node) ?? []) {
if (dep === path[0]) { return [...path, dep]; }

const cycle = recurse(dep, [...path, dep]);
if (cycle) { return cycle; }
}

return undefined;
}
}
4 changes: 2 additions & 2 deletions packages/@aws-cdk/assertions/lib/private/mappings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { filterLogicalId, formatFailure, matchSection } from './section';
import { Template } from './template';

export function findMappings(template: Template, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } {
const section: { [key: string] : {} } = template.Mappings;
const section: { [key: string] : {} } = template.Mappings ?? {};
const result = matchSection(filterLogicalId(section, logicalId), props);

if (!result.match) {
Expand All @@ -13,7 +13,7 @@ export function findMappings(template: Template, logicalId: string, props: any =
}

export function hasMapping(template: Template, logicalId: string, props: any): string | void {
const section: { [key: string]: {} } = template.Mappings;
const section: { [key: string]: {} } = template.Mappings ?? {};
const result = matchSection(filterLogicalId(section, logicalId), props);

if (result.match) {
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/assertions/lib/private/outputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { filterLogicalId, formatFailure, matchSection } from './section';
import { Template } from './template';

export function findOutputs(template: Template, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } {
const section = template.Outputs;
const section = template.Outputs ?? {};
const result = matchSection(filterLogicalId(section, logicalId), props);

if (!result.match) {
Expand All @@ -13,7 +13,7 @@ export function findOutputs(template: Template, logicalId: string, props: any =
}

export function hasOutput(template: Template, logicalId: string, props: any): string | void {
const section: { [key: string]: {} } = template.Outputs;
const section: { [key: string]: {} } = template.Outputs ?? {};
const result = matchSection(filterLogicalId(section, logicalId), props);
if (result.match) {
return;
Expand Down
Loading

0 comments on commit 8f8dc48

Please sign in to comment.