Skip to content

Commit 696dada

Browse files
authored
feat(cli-integ): add region constraints support for Atmosphere (#1029)
This PR adds support for region constraints when acquiring test environments from Atmosphere. The `@cdklabs/cdk-atmosphere-client` is upgraded to 0.0.84 which introduces the `constraints` parameter for environment acquisition. A new `regions` option is added to `AwsContextOptions` that allows tests to specify which AWS regions they require. This is particularly useful for tests that depend on services only available in specific regions. Instead of acquiring any environment and then skipping the test at runtime when the region doesn't support the required service, tests can now request an environment in a supported region upfront. The Bedrock AgentCore Runtime hotswap test introduced in #991 is updated to use this new capability, requesting an environment in one of the regions where the service is available. --- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license
1 parent 943967e commit 696dada

File tree

7 files changed

+822
-48
lines changed

7 files changed

+822
-48
lines changed

packages/@aws-cdk-testing/cli-integ/lib/cli/run-suite.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ async function main() {
8888
type: 'string',
8989
requiresArg: true,
9090
})
91+
.options('seed', {
92+
describe: 'Set the seed value to replicate a test run',
93+
type: 'string',
94+
requiresArg: true,
95+
})
9196
.options('verbose', {
9297
alias: 'v',
9398
describe: 'Run in verbose mode',
@@ -225,6 +230,7 @@ async function main() {
225230

226231
await jest.run([
227232
'--randomize',
233+
...args.seed ? [`--seed=${args.seed}`] : [],
228234
...args.runInBand ? ['-i'] : [],
229235
...args.test ? ['-t', args.test] : [],
230236
...args.verbose ? ['--verbose'] : [],

packages/@aws-cdk-testing/cli-integ/lib/with-aws.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { Constraint } from '@cdklabs/cdk-atmosphere-client';
12
import { AtmosphereClient } from '@cdklabs/cdk-atmosphere-client';
23
import { AwsClients } from './aws';
34
import type { TestContext } from './integ-test';
@@ -27,23 +28,54 @@ export function atmospherePool() {
2728

2829
export type AwsContext = { readonly aws: AwsClients };
2930

31+
export interface AwsContextOptions {
32+
/**
33+
* Request the test environment to be in one of these regions
34+
*
35+
* @default - all regions are possible
36+
*/
37+
readonly regions?: string[];
38+
39+
/**
40+
* Do not bootstrap the env
41+
*
42+
* @default false
43+
*/
44+
readonly disableBootstrap?: boolean;
45+
}
46+
3047
/**
3148
* Higher order function to execute a block with an AWS client setup
3249
*
3350
* Allocate the next region from the REGION pool and dispose it afterwards.
3451
*/
3552
export function withAws<A extends TestContext>(
3653
block: (context: A & AwsContext & DisableBootstrapContext) => Promise<void>,
37-
disableBootstrap: boolean = false,
54+
options: AwsContextOptions = {},
3855
): (context: A) => Promise<void> {
3956
return async (context: A) => {
57+
const disableBootstrap = options.disableBootstrap ?? false;
58+
4059
if (atmosphereEnabled()) {
4160
const atmosphere = new AtmosphereClient(atmosphereEndpoint(), {
4261
logStream: context.output,
4362
});
4463

64+
const constraints: Constraint[] = [];
65+
if (options.regions) {
66+
constraints.push({
67+
type: 'region',
68+
value: [...options.regions],
69+
});
70+
}
71+
4572
const start = Date.now();
46-
const allocation = await atmosphere.acquire({ pool: atmospherePool(), requester: context.name, timeoutSeconds: 60 * 30 });
73+
const allocation = await atmosphere.acquire({
74+
pool: atmospherePool(),
75+
requester: context.name,
76+
timeoutSeconds: 60 * 30,
77+
constraints,
78+
});
4779
let outcome = 'success';
4880
context.reportWaitTime(Date.now() - start);
4981

packages/@aws-cdk-testing/cli-integ/lib/with-cdk-app.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,21 @@ import { testSource } from './package-sources/subprocess';
1414
import { RESOURCES_DIR } from './resources';
1515
import type { ShellOptions } from './shell';
1616
import { shell, ShellHelper, rimraf } from './shell';
17-
import type { AwsContext } from './with-aws';
17+
import type { AwsContext, AwsContextOptions } from './with-aws';
1818
import { atmosphereEnabled, withAws } from './with-aws';
1919
import { withTimeout } from './with-timeout';
2020
import { findYarnPackages } from './yarn';
2121

2222
export const DEFAULT_TEST_TIMEOUT_S = 20 * 60;
2323
export const EXTENDED_TEST_TIMEOUT_S = 30 * 60;
2424

25+
export interface CdkAppContextOptions {
26+
/**
27+
* Configure the AWS context of the app.
28+
*/
29+
readonly aws?: AwsContextOptions;
30+
}
31+
2532
/**
2633
* Higher order function to execute a block with a CDK app fixture
2734
*
@@ -147,20 +154,20 @@ export function withCdkMigrateApp(
147154
* We could have put `withAws(withCdkApp(fixture => { /... actual test here.../ }))` in every
148155
* test declaration but centralizing it is going to make it convenient to modify in the future.
149156
*/
150-
export function withDefaultFixture(block: (context: TestFixture) => Promise<void>) {
151-
return withAws(withTimeout(DEFAULT_TEST_TIMEOUT_S, withCdkApp(block)));
157+
export function withDefaultFixture(block: (context: TestFixture) => Promise<void>, options: CdkAppContextOptions = {}) {
158+
return withAws(withTimeout(DEFAULT_TEST_TIMEOUT_S, withCdkApp(block)), options.aws);
152159
}
153160

154-
export function withSpecificFixture(appName: string, block: (context: TestFixture) => Promise<void>) {
155-
return withAws(withTimeout(DEFAULT_TEST_TIMEOUT_S, withSpecificCdkApp(appName, block)));
161+
export function withSpecificFixture(appName: string, block: (context: TestFixture) => Promise<void>, options: CdkAppContextOptions = {}) {
162+
return withAws(withTimeout(DEFAULT_TEST_TIMEOUT_S, withSpecificCdkApp(appName, block)), options.aws);
156163
}
157164

158-
export function withExtendedTimeoutFixture(block: (context: TestFixture) => Promise<void>) {
159-
return withAws(withTimeout(EXTENDED_TEST_TIMEOUT_S, withCdkApp(block)));
165+
export function withExtendedTimeoutFixture(block: (context: TestFixture) => Promise<void>, options: CdkAppContextOptions = {}) {
166+
return withAws(withTimeout(EXTENDED_TEST_TIMEOUT_S, withCdkApp(block)), options.aws);
160167
}
161168

162-
export function withCDKMigrateFixture(language: string, block: (content: TestFixture) => Promise<void>) {
163-
return withAws(withTimeout(DEFAULT_TEST_TIMEOUT_S, withCdkMigrateApp(language, block)));
169+
export function withCDKMigrateFixture(language: string, block: (content: TestFixture) => Promise<void>, options: CdkAppContextOptions = {}) {
170+
return withAws(withTimeout(DEFAULT_TEST_TIMEOUT_S, withCdkMigrateApp(language, block)), options.aws);
164171
}
165172

166173
/**
@@ -210,8 +217,11 @@ export interface DisableBootstrapContext {
210217
* To be used in place of `withDefaultFixture` when the test
211218
* should not create the default bootstrap stack
212219
*/
213-
export function withoutBootstrap(block: (context: TestFixture) => Promise<void>) {
214-
return withAws(withCdkApp(block), true);
220+
export function withoutBootstrap(block: (context: TestFixture) => Promise<void>, options: CdkAppContextOptions = {}) {
221+
return withAws(withCdkApp(block), {
222+
...options.aws,
223+
disableBootstrap: true,
224+
});
215225
}
216226

217227
export interface CdkCliOptions extends ShellOptions {
@@ -405,6 +415,10 @@ export class TestFixture extends ShellHelper {
405415
});
406416
}
407417

418+
/**
419+
* @returns the captured output of the deploy command.
420+
* !!! DO NOT assume this is the stack's ARN. It will contain other output. !!!
421+
*/
408422
public async cdkDeploy(stackNames: string | string[], options: CdkCliOptions = {}, skipStackRename?: boolean) {
409423
return this.cdk(this.cdkDeployCommandLine(stackNames, options, skipStackRename), options);
410424
}

packages/@aws-cdk-testing/cli-integ/package.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/flags/cdk-flags-with-cli-context.integtest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ integTest(
2020

2121
expect(output).toContain('Flag changes:');
2222
}),
23-
true,
23+
{ disableBootstrap: true },
2424
),
2525
);

packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/hotswap/cdk-hotswap-deployment-supports-bedrock-agentcore-runtime.integtest.ts

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,12 @@ import { integTest, withDefaultFixture } from '../../../lib';
33

44
jest.setTimeout(2 * 60 * 60_000); // Includes the time to acquire locks, worst-case single-threaded runtime
55

6-
// Bedrock AgentCore Runtime is only available in specific regions
7-
// Source: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-regions.html
8-
const SUPPORTED_REGIONS = [
9-
'ap-south-1',
10-
'ap-southeast-1',
11-
'ap-southeast-2',
12-
'ap-northeast-1',
13-
'eu-west-1',
14-
'eu-central-1',
15-
'us-east-1',
16-
'us-east-2',
17-
'us-west-2',
18-
];
19-
206
integTest(
217
'hotswap deployment supports Bedrock AgentCore Runtime',
228
withDefaultFixture(async (fixture) => {
23-
const currentRegion = fixture.aws.region;
24-
if (!SUPPORTED_REGIONS.includes(currentRegion)) {
25-
fixture.log(`Skipping test: Bedrock AgentCore Runtime is not supported in region ${currentRegion}`);
26-
return;
27-
}
28-
299
// GIVEN
30-
const stackArn = await fixture.cdkDeploy('agentcore-hotswap', {
10+
const stackName = 'agentcore-hotswap';
11+
await fixture.cdkDeploy(stackName, {
3112
captureStderr: false,
3213
modEnv: {
3314
DYNAMIC_BEDROCK_RUNTIME_DESCRIPTION: 'original description',
@@ -36,7 +17,7 @@ integTest(
3617
});
3718

3819
// WHEN
39-
const deployOutput = await fixture.cdkDeploy('agentcore-hotswap', {
20+
const deployOutput = await fixture.cdkDeploy(stackName, {
4021
options: ['--hotswap'],
4122
captureStderr: true,
4223
onlyStderr: true,
@@ -46,13 +27,9 @@ integTest(
4627
},
4728
});
4829

49-
// Extract stack name from ARN (format: arn:aws:cloudformation:region:account:stack/name/id)
50-
// Using the full ARN can cause "Stack name cannot exceed 128 characters" error in some cases
51-
const stackName = stackArn.split('/')[1];
52-
5330
const response = await fixture.aws.cloudFormation.send(
5431
new DescribeStacksCommand({
55-
StackName: stackName,
32+
StackName: fixture.fullStackName(stackName),
5633
}),
5734
);
5835
const runtimeId = response.Stacks?.[0].Outputs?.find((output) => output.OutputKey === 'RuntimeId')?.OutputValue;
@@ -65,5 +42,21 @@ integTest(
6542
// The entire string fails locally due to formatting. Making this test less specific
6643
expect(deployOutput).toMatch(/hotswapped!/);
6744
expect(deployOutput).toContain(runtimeId);
45+
}, {
46+
aws: {
47+
// Bedrock AgentCore Runtime is only available in specific regions
48+
// Source: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-regions.html
49+
regions: [
50+
'ap-south-1',
51+
'ap-southeast-1',
52+
'ap-southeast-2',
53+
'ap-northeast-1',
54+
'eu-west-1',
55+
'eu-central-1',
56+
'us-east-1',
57+
'us-east-2',
58+
'us-west-2',
59+
],
60+
},
6861
}),
6962
);

0 commit comments

Comments
 (0)