Skip to content

Commit 5e9f6d0

Browse files
authored
fix(e2e): reap stale OAuth2 credential providers alongside API key providers (#1679)
* fix(e2e): reap stale OAuth2 credential providers in globalSetup The e2e globalSetup already reaps stale API key credential providers, but OAuth2 providers are a separate resource type with their own List/Delete APIs — they do NOT appear in ListApiKeyCredentialProviders, so the existing cleanup can never see them. The CUSTOM_JWT harness test registers a managed OAuth2 provider (`<name>-oauth`) created outside the CloudFormation stack, so teardown leaves it behind. These accumulate against the account's 50-provider OAuth2 quota (L-431051DC) until every CUSTOM_JWT deploy fails with "The number of agent identity Oauth2 credential providers in this account has reached its limit", failing the E2E suite. Add cleanupStaleOAuth2CredentialProviders (mirrors the API key reaper, using ListOauth2CredentialProviders / DeleteOauth2CredentialProvider) and wire it into globalSetup alongside the existing stack / API key / recommendation cleanups. * refactor(e2e): reap both provider types in one cleanupStaleCredentialProviders Fold the OAuth2 reaping into cleanupStaleCredentialProviders rather than a separate function + second globalSetup call. From the caller's view "reap stale credential providers" is one job; splitting it by resource type is what let OAuth2 providers go unreaped in the first place. The function now walks both the API key and OAuth2 list APIs. globalSetup reverts to a single call (no longer changed vs main).
1 parent 2953d61 commit 5e9f6d0

1 file changed

Lines changed: 41 additions & 7 deletions

File tree

e2e-tests/utils/credential-provider-cleanup.ts

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import type { Logger } from './logger';
22
import {
33
BedrockAgentCoreControlClient,
44
DeleteApiKeyCredentialProviderCommand,
5+
DeleteOauth2CredentialProviderCommand,
56
ListApiKeyCredentialProvidersCommand,
7+
ListOauth2CredentialProvidersCommand,
68
} from '@aws-sdk/client-bedrock-agentcore-control';
79

810
export async function deleteCredentialProvider(
@@ -19,6 +21,31 @@ export async function deleteCredentialProvider(
1921
}
2022
}
2123

24+
export async function deleteOAuth2CredentialProvider(
25+
client: BedrockAgentCoreControlClient,
26+
logger: Logger,
27+
name: string
28+
): Promise<void> {
29+
try {
30+
await client.send(new DeleteOauth2CredentialProviderCommand({ name }));
31+
logger.info(`Deleted OAuth2 credential provider: ${name}`);
32+
} catch (error) {
33+
const err = error as Error;
34+
logger.warn(`Failed to delete OAuth2 credential provider ${name}: ${err.name}:${err.message}`);
35+
}
36+
}
37+
38+
/**
39+
* Delete stale credential providers matching `prefix` and older than `minAgeMs`.
40+
*
41+
* Reaps BOTH provider types the e2e suite creates: API key providers and OAuth2 providers.
42+
* These are distinct resource types with separate List/Delete APIs — an OAuth2 provider does
43+
* NOT appear in ListApiKeyCredentialProviders — so both lists must be walked. The CUSTOM_JWT
44+
* harness test registers a managed OAuth2 provider (`<name>-oauth`) created outside the
45+
* CloudFormation stack, so teardown leaves it behind; without reaping it here, they accumulate
46+
* against the account's 50-provider OAuth2 quota (L-431051DC) until every CUSTOM_JWT deploy
47+
* fails with a limit-reached error.
48+
*/
2249
export async function cleanupStaleCredentialProviders(
2350
client: BedrockAgentCoreControlClient,
2451
logger: Logger,
@@ -28,15 +55,22 @@ export async function cleanupStaleCredentialProviders(
2855
}
2956
): Promise<void> {
3057
const cutoff = new Date(Date.now() - options.minAgeMs);
58+
const isStale = (p: { name?: string; createdTime?: Date }): boolean =>
59+
!!p.name?.startsWith(options.prefix) && !!p.createdTime && p.createdTime < cutoff;
3160

32-
let nextToken: string | undefined;
61+
let apiKeyNextToken: string | undefined;
3362
do {
34-
const response = await client.send(new ListApiKeyCredentialProvidersCommand({ nextToken }));
35-
const providers = response.credentialProviders ?? [];
36-
const stale = providers.filter(p => p.name?.startsWith(options.prefix) && p.createdTime && p.createdTime < cutoff);
37-
63+
const response = await client.send(new ListApiKeyCredentialProvidersCommand({ nextToken: apiKeyNextToken }));
64+
const stale = (response.credentialProviders ?? []).filter(isStale);
3865
await Promise.all(stale.map(p => deleteCredentialProvider(client, logger, p.name!)));
66+
apiKeyNextToken = response.nextToken;
67+
} while (apiKeyNextToken);
3968

40-
nextToken = response.nextToken;
41-
} while (nextToken);
69+
let oauth2NextToken: string | undefined;
70+
do {
71+
const response = await client.send(new ListOauth2CredentialProvidersCommand({ nextToken: oauth2NextToken }));
72+
const stale = (response.credentialProviders ?? []).filter(isStale);
73+
await Promise.all(stale.map(p => deleteOAuth2CredentialProvider(client, logger, p.name!)));
74+
oauth2NextToken = response.nextToken;
75+
} while (oauth2NextToken);
4276
}

0 commit comments

Comments
 (0)