Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/@aws-cdk/integ-runner/lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export function parseCliArgs(args: string[] = []) {
.option('proxy', { type: 'string', desc: 'Use the indicated proxy. Will read from HTTPS_PROXY environment variable if not specified', requiresArg: true })
.option('ca-bundle-path', { type: 'string', desc: 'Path to CA certificate to use when validating HTTPS requests. Will read from AWS_CA_BUNDLE environment variable if not specified', requiresArg: true })
.option('unstable', { type: 'array', desc: `Opt-in to using unstable features. By using these flags you acknowledge that scope and API of unstable features may change without notice. Specify multiple times for each unstable feature you want to opt-in to. ${availableFeaturesDescription()}`, nargs: 1, default: [] })
.option('role-arn', { type: 'string', desc: 'ARN of the IAM role for CloudFormation to assume during deploy/destroy', requiresArg: true })
.strict()
.parse(args);

Expand Down Expand Up @@ -119,6 +120,7 @@ export function parseCliArgs(args: string[] = []) {
unstable: arrayFromYargs(argv.unstable) ?? [],
proxy: argv.proxy as (string | undefined),
caBundlePath: argv['ca-bundle-path'] as (string | undefined),
roleArn: argv['role-arn'] as (string | undefined),
};
}

Expand Down Expand Up @@ -200,6 +202,7 @@ async function run(options: ReturnType<typeof parseCliArgs>) {
watch: options.watch,
proxy: options.proxy,
caBundlePath: options.caBundlePath,
roleArn: options.roleArn,
});
testsSucceeded = success;

Expand All @@ -223,6 +226,7 @@ async function run(options: ReturnType<typeof parseCliArgs>) {
region: options.testRegions[0],
proxy: options.proxy,
caBundlePath: options.caBundlePath,
roleArn: options.roleArn,
});
}
} finally {
Expand Down
17 changes: 16 additions & 1 deletion packages/@aws-cdk/integ-runner/lib/runner/integ-test-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ export interface CommonOptions {
}

export interface WatchOptions extends CommonOptions {

/**
* ARN of the IAM role for CloudFormation to assume during deploy/destroy
*
* @default - use the bootstrap cfn-exec role
*/
readonly roleArn?: string;
}

/**
Expand Down Expand Up @@ -82,6 +87,13 @@ export interface RunOptions extends CommonOptions {
* @default true
*/
readonly updateWorkflow?: boolean;

/**
* ARN of the IAM role for CloudFormation to assume during deploy/destroy
*
* @default - use the bootstrap cfn-exec role
*/
readonly roleArn?: string;
}

/**
Expand Down Expand Up @@ -206,6 +218,7 @@ export class IntegTestRunner extends IntegRunner {
traceLogs: enableForVerbosityLevel(2) ?? false,
verbose: enableForVerbosityLevel(3),
debug: enableForVerbosityLevel(4),
roleArn: options.roleArn,
},
options.testCaseName,
options.verbosity ?? 0,
Expand Down Expand Up @@ -250,6 +263,7 @@ export class IntegTestRunner extends IntegRunner {
requireApproval: RequireApproval.NEVER,
verbose: enableForVerbosityLevel(3),
debug: enableForVerbosityLevel(4),
roleArn: options.roleArn,
},
updateWorkflowEnabled,
options.testCaseName,
Expand All @@ -275,6 +289,7 @@ export class IntegTestRunner extends IntegRunner {
output: path.relative(this.directory, this.cdkOutDir),
...actualTestCase.cdkCommandOptions?.destroy?.args,
context: this.getContext(actualTestCase.cdkCommandOptions?.destroy?.args?.context),
roleArn: options.roleArn ?? actualTestCase.cdkCommandOptions?.destroy?.args?.roleArn,
verbose: enableForVerbosityLevel(3),
debug: enableForVerbosityLevel(4),
});
Expand Down
7 changes: 7 additions & 0 deletions packages/@aws-cdk/integ-runner/lib/workers/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ export interface IntegTestOptions {
* @default - no additional CA bundle
*/
readonly caBundlePath?: string;

/**
* ARN of the IAM role for CloudFormation to assume during deploy/destroy
*
* @default - use the bootstrap cfn-exec role
*/
readonly roleArn?: string;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export async function integTestWorker(request: IntegTestBatchRequest): Promise<I
dryRun: request.dryRun,
updateWorkflow: request.updateWorkflow,
verbosity,
roleArn: request.roleArn,
});
if (results && Object.values(results).some(result => result.status === 'fail')) {
failures.push(testInfo);
Expand Down Expand Up @@ -117,6 +118,7 @@ export async function watchTestWorker(options: IntegWatchOptions): Promise<void>
await runner.watchIntegTest({
testCaseName,
verbosity,
roleArn: options.roleArn,
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export async function runIntegrationTestsInParallel(
updateWorkflow: options.updateWorkflow,
proxy: options.proxy,
caBundlePath: options.caBundlePath,
roleArn: options.roleArn,
}], {
on: printResults,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface IntegWatchOptions extends IntegTestInfo {
readonly verbosity?: number;
readonly proxy?: string;
readonly caBundlePath?: string;
readonly roleArn?: string;
}
export async function watchIntegrationTest(pool: workerpool.WorkerPool, options: IntegWatchOptions): Promise<void> {
await pool.exec('watchTestWorker', [options], {
Expand Down
12 changes: 12 additions & 0 deletions packages/@aws-cdk/integ-runner/test/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,3 +317,15 @@ describe('Proxy options', () => {
}
});
});

describe('Role ARN option', () => {
test('--role-arn is parsed', () => {
const options = parseCliArgs(['--role-arn', 'arn:aws:iam::123456789012:role/MyRole']);
expect(options.roleArn).toBe('arn:aws:iam::123456789012:role/MyRole');
});

test('role-arn defaults to undefined', () => {
const options = parseCliArgs([]);
expect(options.roleArn).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -775,3 +775,71 @@ describe('IntegTest watchIntegTest', () => {
}).rejects.toThrow('xxxxx.test-with-error is a new test. Please use the IntegTest construct to configure the test\nhttps://github.com/aws/aws-cdk/tree/main/packages/%40aws-cdk/integ-tests-alpha');
});
});

describe('IntegTest roleArn', () => {
test('roleArn is passed to deploy and destroy', async () => {
// WHEN
const integTest = new IntegTestRunner({
cdk: cdkMock.cdk,
region: 'eu-west-1',
test: new IntegTest({
fileName: 'test/test-data/xxxxx.test-with-snapshot.js',
discoveryRoot: 'test/test-data',
}),
});
await integTest.runIntegTestCase({
testCaseName: 'xxxxx.test-with-snapshot',
roleArn: 'arn:aws:iam::123456789012:role/MyRole',
});

// THEN
expect(cdkMock.mocks.deploy).toHaveBeenCalledWith(expect.objectContaining({
roleArn: 'arn:aws:iam::123456789012:role/MyRole',
}));
expect(cdkMock.mocks.destroy).toHaveBeenCalledWith(expect.objectContaining({
roleArn: 'arn:aws:iam::123456789012:role/MyRole',
}));
});

test('roleArn is passed to watch', async () => {
// WHEN
const integTest = new IntegTestRunner({
cdk: cdkMock.cdk,
region: 'eu-west-1',
test: new IntegTest({
fileName: 'test/test-data/xxxxx.test-with-snapshot.js',
discoveryRoot: 'test/test-data',
}),
});
await integTest.watchIntegTest({
testCaseName: 'xxxxx.test-with-snapshot',
roleArn: 'arn:aws:iam::123456789012:role/MyRole',
});

// THEN
expect(cdkMock.mocks.watch).toHaveBeenCalledWith(expect.objectContaining({
roleArn: 'arn:aws:iam::123456789012:role/MyRole',
}), expect.anything());
});

test('roleArn overrides manifest destroy args', async () => {
// WHEN
const integTest = new IntegTestRunner({
cdk: cdkMock.cdk,
region: 'eu-west-1',
test: new IntegTest({
fileName: 'test/test-data/xxxxx.test-with-snapshot.js',
discoveryRoot: 'test/test-data',
}),
});
await integTest.runIntegTestCase({
testCaseName: 'xxxxx.test-with-snapshot',
roleArn: 'arn:aws:iam::123456789012:role/CliRole',
});

// THEN - CLI roleArn takes precedence
expect(cdkMock.mocks.destroy).toHaveBeenCalledWith(expect.objectContaining({
roleArn: 'arn:aws:iam::123456789012:role/CliRole',
}));
});
});
33 changes: 33 additions & 0 deletions packages/@aws-cdk/integ-runner/test/workers/integ-worker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -615,3 +615,36 @@ describe('parallel worker', () => {
});
});
});

describe('integTestWorker roleArn', () => {
let mockActualTests: jest.Mock;
let mockRunIntegTestCase: jest.Mock;

beforeEach(() => {
mockActualTests = jest.fn();
mockRunIntegTestCase = jest.fn();

jest.spyOn(IntegTestRunner.prototype, 'actualTests').mockImplementation(mockActualTests);
jest.spyOn(IntegTestRunner.prototype, 'runIntegTestCase').mockImplementation(mockRunIntegTestCase);
});

test('passes roleArn to runIntegTestCase', async () => {
mockActualTests.mockResolvedValue({
'test-case-1': { stacks: ['Stack1'] },
});
mockRunIntegTestCase.mockResolvedValue(undefined);

await integTestWorker({
tests: [{
fileName: 'test/test-data/xxxxx.test-with-snapshot.js',
discoveryRoot: 'test/test-data',
}],
region: 'us-east-1',
roleArn: 'arn:aws:iam::123456789012:role/MyRole',
});

expect(mockRunIntegTestCase).toHaveBeenCalledWith(
expect.objectContaining({ roleArn: 'arn:aws:iam::123456789012:role/MyRole' }),
);
});
});
Loading