Skip to content

Commit

Permalink
fix: update tests to the latest version (#33)
Browse files Browse the repository at this point in the history
From commit 085c2e309f475c34877fa4966985472fc25da28b
  • Loading branch information
rix0rrr authored Feb 15, 2025
1 parent a7e12c4 commit 6ee7bfa
Show file tree
Hide file tree
Showing 23 changed files with 867 additions and 132 deletions.
1 change: 1 addition & 0 deletions .projenrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ const cliInteg = configureProject(
'@aws-sdk/credential-providers@^3',
'@smithy/util-retry@^3',
'@smithy/types@^3',
'@cdklabs/cdk-atmosphere-client',
'axios@^1',
'chalk@^4',
'fs-extra@^9',
Expand Down
4 changes: 4 additions & 0 deletions packages/@aws-cdk-testing/cli-integ/.projen/deps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/@aws-cdk-testing/cli-integ/.projen/tasks.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 28 additions & 10 deletions packages/@aws-cdk-testing/cli-integ/lib/aws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,18 @@ import {
import { SNSClient } from '@aws-sdk/client-sns';
import { SSOClient } from '@aws-sdk/client-sso';
import { STSClient, GetCallerIdentityCommand } from '@aws-sdk/client-sts';
import { fromIni } from '@aws-sdk/credential-providers';
import type { AwsCredentialIdentityProvider } from '@smithy/types';
import { fromIni, fromNodeProviderChain } from '@aws-sdk/credential-providers';
import type { AwsCredentialIdentity, AwsCredentialIdentityProvider } from '@smithy/types';
import { ConfiguredRetryStrategy } from '@smithy/util-retry';
interface ClientConfig {
readonly credentials?: AwsCredentialIdentityProvider;
readonly credentials: AwsCredentialIdentityProvider | AwsCredentialIdentity;
readonly region: string;
readonly retryStrategy: ConfiguredRetryStrategy;
}

export class AwsClients {
public static async default(output: NodeJS.WritableStream) {
const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? 'us-east-1';
return AwsClients.forRegion(region, output);
public static async forIdentity(region: string, identity: AwsCredentialIdentity, output: NodeJS.WritableStream) {
return new AwsClients(region, output, identity);
}

public static async forRegion(region: string, output: NodeJS.WritableStream) {
Expand All @@ -50,9 +49,12 @@ export class AwsClients {
public readonly lambda: LambdaClient;
public readonly sts: STSClient;

constructor(public readonly region: string, private readonly output: NodeJS.WritableStream) {
constructor(
public readonly region: string,
private readonly output: NodeJS.WritableStream,
public readonly identity?: AwsCredentialIdentity) {
this.config = {
credentials: chainableCredentials(this.region),
credentials: this.identity ?? chainableCredentials(this.region),
region: this.region,
retryStrategy: new ConfiguredRetryStrategy(9, (attempt: number) => attempt ** 500),
};
Expand All @@ -78,6 +80,17 @@ export class AwsClients {
return (await stsClient.send(new GetCallerIdentityCommand({}))).Account!;
}

/**
* Resolve the current identity or identity provider to credentials
*/
public async credentials() {
const x = this.config.credentials;
if (isAwsCredentialIdentity(x)) {
return x;
}
return x();
}

public async deleteStacks(...stackNames: string[]) {
if (stackNames.length === 0) {
return;
Expand Down Expand Up @@ -249,7 +262,7 @@ export async function sleep(ms: number) {
return new Promise((ok) => setTimeout(ok, ms));
}

function chainableCredentials(region: string): AwsCredentialIdentityProvider | undefined {
function chainableCredentials(region: string): AwsCredentialIdentityProvider {
if ((process.env.CODEBUILD_BUILD_ARN || process.env.GITHUB_RUN_ID) && process.env.AWS_PROFILE) {
// in codebuild we must assume the role that the cdk uses
// otherwise credentials will just be picked up by the normal sdk
Expand All @@ -259,5 +272,10 @@ function chainableCredentials(region: string): AwsCredentialIdentityProvider | u
});
}

return undefined;
// Otherwise just get what's default
return fromNodeProviderChain({ clientConfig: { region } });
}

function isAwsCredentialIdentity(x: any): x is AwsCredentialIdentity {
return Boolean(x && typeof x === 'object' && x.accessKeyId);
}
2 changes: 1 addition & 1 deletion packages/@aws-cdk-testing/cli-integ/lib/cli/run-suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,13 @@ async function main() {

try {
await jest.run([
'--randomize',
...args.runInBand ? ['-i'] : [],
...args.test ? ['-t', args.test] : [],
...args.verbose ? ['--verbose'] : [],
...passWithNoTests ? ['--passWithNoTests'] : [],
...args['test-file'] ? [args['test-file']] : [],
], path.resolve(__dirname, '..', '..', 'resources', 'integ.jest.config.js'));

} finally {
await packageSource.cleanup();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ async function main() {
type: 'string',
requiresArg: true,
}), async (args) => {

await validateDirectory(args);
const repo = await (args.name ? TestRepository.newWithName(args.name) : TestRepository.newRandom());
const usageDir = UsageDir.default();
Expand All @@ -71,7 +70,6 @@ async function main() {
requiresArg: true,
demandOption: true,
}), async (args) => {

const repo = TestRepository.existing(args.name);
const usageDir = UsageDir.default();

Expand Down Expand Up @@ -99,7 +97,6 @@ async function main() {
default: true,
requiresArg: false,
}), async (args) => {

await validateDirectory(args);
const repo = await TestRepository.newRandom();
const usageDir = UsageDir.default();
Expand All @@ -114,7 +111,6 @@ async function main() {
shell: true,
show: 'always',
});

} finally {
if (args.cleanup) {
await repo.delete();
Expand All @@ -128,7 +124,6 @@ async function main() {
type: 'string',
requiresArg: true,
}), async (args) => {

const usageDir = UsageDir.default();

let repositoryName = args.name;
Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk-testing/cli-integ/lib/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ export async function fetchPreviousVersion(token: string, options?: {
if (previousMVRelease) { return previousMVRelease; }

throw new Error(`Unable to find previous version given ${JSON.stringify(options)}`);
};
}
5 changes: 4 additions & 1 deletion packages/@aws-cdk-testing/cli-integ/lib/integ-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ let failed = false;

export interface TestContext {
readonly randomString: string;
readonly name: string;
readonly output: NodeJS.WritableStream;
log(s: string): void;
};
}

if (process.env.JEST_TEST_CONCURRENT === 'true') {
process.stderr.write('ℹ️ JEST_TEST_CONCURRENT is true: tests will run concurrently and filters have no effect!\n0');
Expand Down Expand Up @@ -61,6 +62,7 @@ export function integTest(
return await callback({
output,
randomString: randomString(),
name,
log(s: string) {
output.write(`${s}\n`);
},
Expand Down Expand Up @@ -98,6 +100,7 @@ export function integTest(
} else {
// Use 'console.log' so the output is buffered by
// jest and prints without a stack trace (if verbose: false).
// eslint-disable-next-line no-console
console.log(output.buffer().toString());
}
throw e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import { shell, rimraf, addToShellPath } from '../shell';

export class ReleasePackageSourceSetup implements IPackageSourceSetup {
readonly name = 'release';
readonly description = `release @ ${this.version}`;
readonly description: string;

private tempDir?: string;

constructor(private readonly version: string, private readonly frameworkVersion?: string) {
this.description = `release @ ${this.version}`;
}

public async prepare(): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import { shell, addToShellPath } from '../shell';

export class RepoPackageSourceSetup implements IPackageSourceSetup {
readonly name = 'repo';
readonly description = `repo(${this.repoRoot})`;
readonly description: string;

constructor(private readonly repoRoot: string) {
this.description = `repo(${this.repoRoot})`;
}

public async prepare(): Promise<void> {
Expand Down Expand Up @@ -75,10 +76,10 @@ async function writePackageMap(repoRoot: string): Promise<string> {
const YARN_MONOREPO_CACHE: Record<string, any> = {};

/**
* Return a { name -> directory } packages found in a Yarn monorepo
*
* Cached in YARN_MONOREPO_CACHE.
*/
* Return a { name -> directory } packages found in a Yarn monorepo
*
* Cached in YARN_MONOREPO_CACHE.
*/
export async function findYarnPackages(root: string): Promise<Record<string, string>> {
if (!(root in YARN_MONOREPO_CACHE)) {
const outputDataString: string = JSON.parse(await shell(['yarn', 'workspaces', '--json', 'info'], {
Expand Down
64 changes: 64 additions & 0 deletions packages/@aws-cdk-testing/cli-integ/lib/proxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { promises as fs } from 'fs';
import * as querystring from 'node:querystring';
import * as os from 'os';
import * as path from 'path';
import * as mockttp from 'mockttp';
import { CompletedRequest } from 'mockttp';

export async function startProxyServer(certDirRoot?: string): Promise<ProxyServer> {
const certDir = await fs.mkdtemp(path.join(certDirRoot ?? os.tmpdir(), 'cdk-'));
const certPath = path.join(certDir, 'cert.pem');
const keyPath = path.join(certDir, 'key.pem');

// Set up key and certificate
const { key, cert } = await mockttp.generateCACertificate();
await fs.writeFile(keyPath, key);
await fs.writeFile(certPath, cert);

const server = mockttp.getLocal({
https: { keyPath: keyPath, certPath: certPath },
});

// We don't need to modify any request, so the proxy
// passes through all requests to the target host.
const endpoint = await server
.forAnyRequest()
.thenPassThrough();

const port = 9000 + Math.floor(Math.random() * 10000);

// server.enableDebug();
await server.start(port);

return {
certPath,
keyPath,
server,
url: server.url,
port: server.port,
getSeenRequests: () => endpoint.getSeenRequests(),
async stop() {
await server.stop();
await fs.rm(certDir, { recursive: true, force: true });
},
};
}

export interface ProxyServer {
readonly certPath: string;
readonly keyPath: string;
readonly server: mockttp.Mockttp;
readonly url: string;
readonly port: number;

getSeenRequests(): Promise<CompletedRequest[]>;
stop(): Promise<void>;
}

export function awsActionsFromRequests(requests: CompletedRequest[]): string[] {
return [...new Set(requests
.map(req => req.body.buffer.toString('utf-8'))
.map(body => querystring.decode(body))
.map(x => x.Action as string)
.filter(action => action != null))];
}
57 changes: 52 additions & 5 deletions packages/@aws-cdk-testing/cli-integ/lib/with-aws.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
import { AtmosphereClient } from '@cdklabs/cdk-atmosphere-client';
import { AwsClients } from './aws';
import { TestContext } from './integ-test';
import { ResourcePool } from './resource-pool';
import { DisableBootstrapContext } from './with-cdk-app';

export function atmosphereEnabled(): boolean {
const enabled = process.env.CDK_INTEG_ATMOSPHERE_ENABLED;
return enabled === 'true' || enabled === '1';
}

export function atmosphereEndpoint(): string {
const value = process.env.CDK_INTEG_ATMOSPHERE_ENDPOINT;
if (!value) {
throw new Error('CDK_INTEG_ATMOSPHERE_ENDPOINT is not defined');
}
return value;
}

export function atmospherePool() {
const value = process.env.CDK_INTEG_ATMOSPHERE_POOL;
if (!value) {
throw new Error('CDK_INTEG_ATMOSPHERE_POOL is not defined');
}
return value;
}

export type AwsContext = { readonly aws: AwsClients };

/**
Expand All @@ -14,12 +36,37 @@ export function withAws<A extends TestContext>(
block: (context: A & AwsContext & DisableBootstrapContext) => Promise<void>,
disableBootstrap: boolean = false,
): (context: A) => Promise<void> {
return (context: A) => regionPool().using(async (region) => {
const aws = await AwsClients.forRegion(region, context.output);
await sanityCheck(aws);
return async (context: A) => {
if (atmosphereEnabled()) {
const atmosphere = new AtmosphereClient(atmosphereEndpoint());
const allocation = await atmosphere.acquire({ pool: atmospherePool(), requester: context.name });
const aws = await AwsClients.forIdentity(allocation.environment.region, {
accessKeyId: allocation.credentials.accessKeyId,
secretAccessKey: allocation.credentials.secretAccessKey,
sessionToken: allocation.credentials.sessionToken,
accountId: allocation.environment.account,
}, context.output);

await sanityCheck(aws);

return block({ ...context, disableBootstrap, aws });
});
let outcome = 'success';
try {
return await block({ ...context, disableBootstrap, aws });
} catch (e: any) {
outcome = 'failure';
throw e;
} finally {
await atmosphere.release(allocation.id, outcome);
}
} else {
return regionPool().using(async (region) => {
const aws = await AwsClients.forRegion(region, context.output);
await sanityCheck(aws);

return block({ ...context, disableBootstrap, aws });
});
}
};
}

let _regionPool: undefined | ResourcePool;
Expand Down
Loading

0 comments on commit 6ee7bfa

Please sign in to comment.