Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into scheduler-doc-updates
Browse files Browse the repository at this point in the history
kaizencc authored Oct 30, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents 96347fb + bc67866 commit dd3c04b
Showing 11 changed files with 176 additions and 74 deletions.
8 changes: 4 additions & 4 deletions packages/@aws-cdk-testing/cli-integ/lib/with-aws.ts
Original file line number Diff line number Diff line change
@@ -10,11 +10,11 @@ export type AwsContext = { readonly aws: AwsClients };
*
* Allocate the next region from the REGION pool and dispose it afterwards.
*/
export function withAws(
block: (context: TestContext & AwsContext & DisableBootstrapContext) => Promise<void>,
export function withAws<A extends TestContext>(
block: (context: A & AwsContext & DisableBootstrapContext) => Promise<void>,
disableBootstrap: boolean = false,
): (context: TestContext) => Promise<void> {
return (context: TestContext) => regionPool().using(async (region) => {
): (context: A) => Promise<void> {
return (context: A) => regionPool().using(async (region) => {
const aws = await AwsClients.forRegion(region, context.output);
await sanityCheck(aws);

Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { promises as fs } from 'fs';
import * as path from 'path';
import { integTest, withTemporaryDirectory, ShellHelper, withPackages, TemporaryDirectoryContext } from '../../lib';
import { withToolContext } from './with-tool-context';
import { integTest, ShellHelper, TemporaryDirectoryContext } from '../../lib';

const TIMEOUT = 1800_000;

integTest('amplify integration', withTemporaryDirectory(withPackages(async (context) => {
integTest('amplify integration', withToolContext(async (context) => {
const shell = ShellHelper.fromContext(context);

await shell.shell(['npm', 'create', '-y', 'amplify@latest']);
@@ -14,9 +15,24 @@ integTest('amplify integration', withTemporaryDirectory(withPackages(async (cont
await updateCdkDependency(context, context.packages.requestedCliVersion(), context.packages.requestedFrameworkVersion());
await shell.shell(['npm', 'install']);

await shell.shell(['npx', 'ampx', 'sandbox', '--once']);
await shell.shell(['npx', 'ampx', 'sandbox', 'delete', '--yes']);
})), TIMEOUT);
await shell.shell(['npx', 'ampx', 'sandbox', '--once'], {
modEnv: {
AWS_REGION: context.aws.region,
},
});
try {

// Future code goes here, putting the try/finally here already so it doesn't
// get forgotten.

} finally {
await shell.shell(['npx', 'ampx', 'sandbox', 'delete', '--yes'], {
modEnv: {
AWS_REGION: context.aws.region,
},
});
}
}), TIMEOUT);

async function updateCdkDependency(context: TemporaryDirectoryContext, cliVersion: string, libVersion: string) {
const filename = path.join(context.integTestDir, 'package.json');
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { TestContext } from '../../lib/integ-test';
import { AwsContext, withAws } from '../../lib/with-aws';
import { DisableBootstrapContext } from '../../lib/with-cdk-app';
import { PackageContext, withPackages } from '../../lib/with-packages';
import { TemporaryDirectoryContext, withTemporaryDirectory } from '../../lib/with-temporary-directory';

/**
* The default prerequisites for tests running tool integrations
*/
export function withToolContext<A extends TestContext>(
block: (context: A & TemporaryDirectoryContext & PackageContext & AwsContext & DisableBootstrapContext
) => Promise<void>) {
return withAws(withTemporaryDirectory(withPackages(block)));
}
52 changes: 23 additions & 29 deletions packages/@aws-cdk/aws-location-alpha/lib/place-index.ts
Original file line number Diff line number Diff line change
@@ -74,39 +74,12 @@ export enum IntendedUse {
STORAGE = 'Storage',
}

abstract class PlaceIndexBase extends Resource implements IPlaceIndex {
public abstract readonly placeIndexName: string;
public abstract readonly placeIndexArn: string;

/**
* Grant the given principal identity permissions to perform the actions on this place index.
*/
public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant {
return iam.Grant.addToPrincipal({
grantee: grantee,
actions: actions,
resourceArns: [this.placeIndexArn],
});
}

/**
* Grant the given identity permissions to search using this index
*/
public grantSearch(grantee: iam.IGrantable): iam.Grant {
return this.grant(grantee,
'geo:SearchPlaceIndexForPosition',
'geo:SearchPlaceIndexForSuggestions',
'geo:SearchPlaceIndexForText',
);
}
}

/**
* A Place Index
*
* @see https://docs.aws.amazon.com/location/latest/developerguide/places-concepts.html
*/
export class PlaceIndex extends PlaceIndexBase {
export class PlaceIndex extends Resource implements IPlaceIndex {
/**
* Use an existing place index by name
*/
@@ -130,7 +103,7 @@ export class PlaceIndex extends PlaceIndexBase {
throw new Error(`Place Index Arn ${placeIndexArn} does not have a resource name.`);
}

class Import extends PlaceIndexBase {
class Import extends Resource implements IPlaceIndex {
public readonly placeIndexName = parsedArn.resourceName!;
public readonly placeIndexArn = placeIndexArn;
}
@@ -187,4 +160,25 @@ export class PlaceIndex extends PlaceIndexBase {
this.placeIndexUpdateTime = placeIndex.attrUpdateTime;
}

/**
* Grant the given principal identity permissions to perform the actions on this place index.
*/
public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant {
return iam.Grant.addToPrincipal({
grantee: grantee,
actions: actions,
resourceArns: [this.placeIndexArn],
});
}

/**
* Grant the given identity permissions to search using this index
*/
public grantSearch(grantee: iam.IGrantable): iam.Grant {
return this.grant(grantee,
'geo:SearchPlaceIndexForPosition',
'geo:SearchPlaceIndexForSuggestions',
'geo:SearchPlaceIndexForText',
);
}
}
2 changes: 1 addition & 1 deletion packages/aws-cdk-lib/core/lib/app.ts
Original file line number Diff line number Diff line change
@@ -192,7 +192,7 @@ export class App extends Stage {
if (autoSynth) {
// synth() guarantees it will only execute once, so a default of 'true'
// doesn't bite manual calling of the function.
process.once('beforeExit', () => this.synth());
process.once('beforeExit', () => this.synth({ errorOnDuplicateSynth: false }));
}

this._treeMetadata = props.treeMetadata ?? true;
61 changes: 60 additions & 1 deletion packages/aws-cdk-lib/core/lib/stage.ts
Original file line number Diff line number Diff line change
@@ -146,6 +146,11 @@ export class Stage extends Construct {
*/
private assembly?: cxapi.CloudAssembly;

/**
* The cached set of construct paths. Empty if assembly was not yet built.
*/
private constructPathsCache: Set<string>;

/**
* Validation plugins to run during synthesis. If any plugin reports any violation,
* synthesis will be interrupted and the report displayed to the user.
@@ -163,6 +168,7 @@ export class Stage extends Construct {

Object.defineProperty(this, STAGE_SYMBOL, { value: true });

this.constructPathsCache = new Set<string>();
this.parentStage = Stage.of(this);

this.region = props.env?.region ?? this.parentStage?.region;
@@ -210,16 +216,62 @@ export class Stage extends Construct {
* calls will return the same assembly.
*/
public synth(options: StageSynthesisOptions = { }): cxapi.CloudAssembly {
if (!this.assembly || options.force) {

let newConstructPaths = this.listAllConstructPaths(this);

// If the assembly cache is uninitiazed, run synthesize and reset construct paths cache
if (this.constructPathsCache.size == 0 || !this.assembly || options.force) {
this.assembly = synthesize(this, {
skipValidation: options.skipValidation,
validateOnSynthesis: options.validateOnSynthesis,
});
newConstructPaths = this.listAllConstructPaths(this);
this.constructPathsCache = newConstructPaths;
}

// If the construct paths set has changed
if (!this.constructPathSetsAreEqual(this.constructPathsCache, newConstructPaths)) {
const errorMessage = 'Synthesis has been called multiple times and the construct tree was modified after the first synthesis.';
if (options.errorOnDuplicateSynth ?? true) {
throw new Error(errorMessage + ' This is not allowed. Remove multple synth() calls and do not modify the construct tree after the first synth().');
} else {
// eslint-disable-next-line no-console
console.error(errorMessage + ' Only the results of the first synth() call are used, and modifications done after it are ignored. Avoid construct tree mutations after synth() has been called unless this is intentional.');
}
}

// Reset construct paths cache
this.constructPathsCache = newConstructPaths;

return this.assembly;
}

// Function that lists all construct paths and returns them as a set
private listAllConstructPaths(construct: IConstruct): Set<string> {
const paths = new Set<string>();
function recurse(root: IConstruct) {
paths.add(root.node.path);
for (const child of root.node.children) {
if (!Stage.isStage(child)) {
recurse(child);
}
}
}
recurse(construct);
return paths;
}

// Checks if sets of construct paths are equal
private constructPathSetsAreEqual(set1: Set<string>, set2: Set<string>): boolean {
if (set1.size !== set2.size) return false;
for (const id of set1) {
if (!set2.has(id)) {
return false;
}
}
return true;
}

private createBuilder(outdir?: string) {
// cannot specify "outdir" if we are a nested stage
if (this.parentStage && outdir) {
@@ -259,4 +311,11 @@ export interface StageSynthesisOptions {
* @default false
*/
readonly force?: boolean;

/**
* Whether or not to throw a warning instead of an error if the construct tree has
* been mutated since the last synth.
* @default true
*/
readonly errorOnDuplicateSynth?: boolean;
}
25 changes: 25 additions & 0 deletions packages/aws-cdk-lib/core/test/synthesis.test.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ import * as os from 'os';
import * as path from 'path';
import { testDeprecated } from '@aws-cdk/cdk-build-tools';
import { Construct } from 'constructs';
import { Template } from '../../assertions';
import * as cxschema from '../../cloud-assembly-schema';
import * as cxapi from '../../cx-api';
import * as cdk from '../lib';
@@ -362,6 +363,30 @@ describe('synthesis', () => {

});

test('calling synth multiple times errors if construct tree is mutated', () => {
const app = new cdk.App();

const stages = [
{
stage: 'PROD',
},
{
stage: 'BETA',
},
];

// THEN - no error the first time synth is called
let stack = new cdk.Stack(app, `${stages[0].stage}-Stack`, {});
expect(() => {
Template.fromStack(stack);
}).not.toThrow();

// THEN - error is thrown since synth was called with mutated stack name
stack = new cdk.Stack(app, `${stages[1].stage}-Stack`, {});
expect(() => {
Template.fromStack(stack);
}).toThrow('Synthesis has been called multiple times and the construct tree was modified after the first synthesis');
});
});

function list(outdir: string) {
42 changes: 21 additions & 21 deletions packages/aws-cdk-lib/pipelines/test/compliance/synths.test.ts
Original file line number Diff line number Diff line change
@@ -186,6 +186,27 @@ test('CodeBuild: environment variables specified in multiple places are correctl
}),
});

new ModernTestGitHubNpmPipeline(pipelineStack, 'Cdk-2', {
synth: new cdkp.CodeBuildStep('Synth', {
input: cdkp.CodePipelineSource.gitHub('test/test', 'main'),
primaryOutputDirectory: '.',
env: {
SOME_ENV_VAR: 'SomeValue',
},
installCommands: [
'install1',
'install2',
],
commands: ['synth'],
buildEnvironment: {
environmentVariables: {
INNER_VAR: { value: 'InnerValue' },
},
privileged: true,
},
}),
});

// THEN
Template.fromStack(pipelineStack).hasResourceProperties('AWS::CodeBuild::Project', {
Environment: Match.objectLike({
@@ -217,27 +238,6 @@ test('CodeBuild: environment variables specified in multiple places are correctl
},
});

new ModernTestGitHubNpmPipeline(pipelineStack, 'Cdk-2', {
synth: new cdkp.CodeBuildStep('Synth', {
input: cdkp.CodePipelineSource.gitHub('test/test', 'main'),
primaryOutputDirectory: '.',
env: {
SOME_ENV_VAR: 'SomeValue',
},
installCommands: [
'install1',
'install2',
],
commands: ['synth'],
buildEnvironment: {
environmentVariables: {
INNER_VAR: { value: 'InnerValue' },
},
privileged: true,
},
}),
});

// THEN
Template.fromStack(pipelineStack).hasResourceProperties('AWS::CodeBuild::Project', {
Environment: Match.objectLike({
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
{
"compilerOptions": {
"target": "ES2020",
"target": "ES2022",
"module": "commonjs",
"lib": [
"es2020",
"ES2023",
"dom"
],
"declaration": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noImplicitReturns": true,
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
{
"compilerOptions": {
"target": "ES2020",
"target": "ES2022",
"module": "commonjs",
"lib": [
"es2020",
"ES2023",
"dom"
],
"declaration": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noImplicitReturns": true,
4 changes: 3 additions & 1 deletion tools/@aws-cdk/spec2cdk/README.md
Original file line number Diff line number Diff line change
@@ -29,7 +29,9 @@ At a code level, import `@aws-cdk/spec2cdk/lib/cfn2ts` for a drop-in replacement
## Temporary Schemas

You can import additional, temporary CloudFormation Registry Schemas to test new functionality that is not yet published in `@aws-cdk/aws-service-spec`.
To do this, drop the schema file into `temporary-schemas/us-east-1` and it will be imported on top of the default model.
To do this, drop the schema file into `temporary-schemas/us-east-1` ([e.g](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resource-type-schemas.html)). After you add the schema file, run spec2cdk with the specified module and short name.
As an example, if you were updating [AWS::KMS::Key](https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-kms), you must run:
`bin/spec2cdk --service AWS::KMS`.

## CLI

0 comments on commit dd3c04b

Please sign in to comment.