|
9 | 9 | import { |
10 | 10 | BedrockAgentCoreControlClient, |
11 | 11 | DeleteApiKeyCredentialProviderCommand, |
| 12 | + ListApiKeyCredentialProvidersCommand, |
12 | 13 | } from '@aws-sdk/client-bedrock-agentcore-control'; |
13 | 14 | import { execSync } from 'node:child_process'; |
14 | 15 | import { randomUUID } from 'node:crypto'; |
@@ -39,6 +40,9 @@ export function createE2ESuite(cfg: E2EConfig) { |
39 | 40 | beforeAll(async () => { |
40 | 41 | if (!canRun) return; |
41 | 42 |
|
| 43 | + // Purge leaked credential providers from prior CI runs to avoid quota limits |
| 44 | + await cleanupStaleCredentialProviders(); |
| 45 | + |
42 | 46 | testDir = join(tmpdir(), `agentcore-e2e-${randomUUID()}`); |
43 | 47 | await mkdir(testDir, { recursive: true }); |
44 | 48 |
|
@@ -292,20 +296,53 @@ export function installCdkTarball(projectPath: string): void { |
292 | 296 | } |
293 | 297 |
|
294 | 298 | export async function teardownE2EProject(projectPath: string, agentName: string, modelProvider: string): Promise<void> { |
295 | | - await spawnAndCollect('agentcore', ['remove', 'all', '--json'], projectPath); |
296 | | - const result = await spawnAndCollect('agentcore', ['deploy', '--yes', '--json'], projectPath); |
297 | | - if (result.exitCode !== 0) { |
298 | | - console.log('Teardown stdout:', result.stdout); |
299 | | - console.log('Teardown stderr:', result.stderr); |
300 | | - } |
| 299 | + // Delete the API key credential provider FIRST — CFN teardown may fail, |
| 300 | + // and leaked providers accumulate until the account hits its quota limit. |
301 | 301 | if (modelProvider !== 'Bedrock' && agentName) { |
302 | 302 | const providerName = `${agentName}${modelProvider}`; |
303 | 303 | const region = process.env.AWS_REGION ?? 'us-east-1'; |
304 | 304 | try { |
305 | 305 | const client = new BedrockAgentCoreControlClient({ region }); |
306 | 306 | await client.send(new DeleteApiKeyCredentialProviderCommand({ name: providerName })); |
307 | | - } catch { |
308 | | - // Best-effort cleanup |
| 307 | + console.log(`Deleted credential provider: ${providerName}`); |
| 308 | + } catch (err) { |
| 309 | + console.warn(`Failed to delete credential provider ${providerName}:`, err); |
| 310 | + } |
| 311 | + } |
| 312 | + |
| 313 | + await spawnAndCollect('agentcore', ['remove', 'all', '--json'], projectPath); |
| 314 | + const result = await spawnAndCollect('agentcore', ['deploy', '--yes', '--json'], projectPath); |
| 315 | + if (result.exitCode !== 0) { |
| 316 | + console.log('Teardown stdout:', result.stdout); |
| 317 | + console.log('Teardown stderr:', result.stderr); |
| 318 | + } |
| 319 | +} |
| 320 | + |
| 321 | +/** |
| 322 | + * Delete stale E2e* API key credential providers left behind by previous |
| 323 | + * CI runs whose teardown failed. Call this before tests to prevent quota |
| 324 | + * exhaustion in the shared test account. |
| 325 | + */ |
| 326 | +export async function cleanupStaleCredentialProviders(): Promise<void> { |
| 327 | + const region = process.env.AWS_REGION ?? 'us-east-1'; |
| 328 | + try { |
| 329 | + const client = new BedrockAgentCoreControlClient({ region }); |
| 330 | + const response = await client.send(new ListApiKeyCredentialProvidersCommand({})); |
| 331 | + const providers = response.credentialProviders ?? []; |
| 332 | + const stale = providers.filter(p => p.name?.startsWith('E2e')); |
| 333 | + |
| 334 | + if (stale.length === 0) return; |
| 335 | + |
| 336 | + console.log(`Cleaning up ${stale.length} stale E2e* credential provider(s)...`); |
| 337 | + for (const provider of stale) { |
| 338 | + try { |
| 339 | + await client.send(new DeleteApiKeyCredentialProviderCommand({ name: provider.name! })); |
| 340 | + console.log(` Deleted: ${provider.name}`); |
| 341 | + } catch (err) { |
| 342 | + console.warn(` Failed to delete ${provider.name}:`, err); |
| 343 | + } |
309 | 344 | } |
| 345 | + } catch (err) { |
| 346 | + console.warn('Failed to list credential providers for cleanup:', err); |
310 | 347 | } |
311 | 348 | } |
0 commit comments