11/* eslint-disable spellcheck/spell-checker, camelcase, @typescript-eslint/no-explicit-any */
2- import { CodeBuild } from 'aws-sdk' ;
2+ import { CodeBuild , Account } from 'aws-sdk' ;
33import { config } from 'dotenv' ;
44import yargs from 'yargs' ;
55import * as aws from 'aws-sdk' ;
@@ -8,29 +8,31 @@ import fs from 'fs-extra';
88import path from 'path' ;
99import { deleteS3Bucket , sleep } from '@aws-amplify/amplify-codegen-e2e-core' ;
1010
11- // Ensure to update scripts/split-e2e-tests.ts is also updated this gets updated
12- const AWS_REGIONS_TO_RUN_TESTS = [
13- 'ap-east-1' ,
14- 'ap-northeast-1' ,
15- 'ap-northeast-2' ,
16- 'ap-northeast-3' ,
17- 'ap-south-1' ,
18- 'ap-southeast-1' ,
19- 'ap-southeast-2' ,
20- 'ca-central-1' ,
21- 'eu-central-1' ,
22- 'eu-north-1' ,
23- 'eu-south-1' ,
24- 'eu-west-1' ,
25- 'eu-west-2' ,
26- 'eu-west-3' ,
27- 'me-south-1' ,
28- 'sa-east-1' ,
29- 'us-east-1' ,
30- 'us-east-2' ,
31- 'us-west-1' ,
32- 'us-west-2' ,
33- ] ;
11+ /**
12+ * Supported regions:
13+ * - All Amplify regions, as reported https://docs.aws.amazon.com/general/latest/gr/amplify.html
14+ *
15+ * NOTE:
16+ * - 'ap-east-1' is not included in the list due to known discrepancy in Amplify CLI 'configure' command dropdown and supported regions
17+ * - Since 'ap-east-1' is not available via 'amplify configure', test $CLI_REGION with 'ap-east-1' will run in 'us-east-1'
18+ * and fail Amplify profile assertion in test setup phase
19+ *
20+ * The list of supported regions must be kept in sync amongst all of:
21+ * - Amplify CLI 'amplify configure' command regions dropdown
22+ * - the internal pipeline that publishes new lambda layer versions
23+ * - amplify-codegen/scripts/e2e-test-regions.json
24+ * - amplify-codegen/scripts/split-canary-tests.ts
25+ * - amplify-codegen/scripts/split-e2e-tests.ts
26+ */
27+ const REPO_ROOT = path . join ( __dirname , '..' , '..' , '..' ) ;
28+ const SUPPORTED_REGIONS_PATH = path . join ( REPO_ROOT , 'scripts' , 'e2e-test-regions.json' ) ;
29+ const AWS_REGIONS_TO_RUN_TESTS_METADATA : TestRegion [ ] = JSON . parse ( fs . readFileSync ( SUPPORTED_REGIONS_PATH , 'utf-8' ) ) ;
30+ const AWS_REGIONS_TO_RUN_TESTS = AWS_REGIONS_TO_RUN_TESTS_METADATA . map ( region => region . name ) ;
31+
32+ type TestRegion = {
33+ name : string ;
34+ optIn : boolean ;
35+ } ;
3436
3537const reportPathDir = path . normalize ( path . join ( __dirname , '..' , 'amplify-e2e-reports' ) ) ;
3638
@@ -97,7 +99,7 @@ type AWSAccountInfo = {
9799} ;
98100
99101const BUCKET_TEST_REGEX = / t e s t / ;
100- const IAM_TEST_REGEX = / ! R o t a t e E 2 e A w s T o k e n - e 2 e T e s t C o n t e x t R o l e | - i n t e g t e s t $ | ^ a m p l i f y - | ^ e u - | ^ u s - | ^ a p - / ;
102+ const IAM_TEST_REGEX = / - R o t a t e E 2 e A w s T o k e n - e 2 e T e s t C o n t e x t R o l e | - i n t e g t e s t | ^ a m p l i f y - / ;
101103const STALE_DURATION_MS = 2 * 60 * 60 * 1000 ; // 2 hours in milliseconds
102104
103105const isCI = ( ) : boolean => process . env . CI && process . env . CODEBUILD ? true : false ;
@@ -164,7 +166,23 @@ const getOrphanTestIamRoles = async (account: AWSAccountInfo): Promise<IamRoleIn
164166 } ,
165167 ...( region ? { region } : { } ) ,
166168 maxRetries : 10 ,
167- } ) ;
169+ } ) ;
170+
171+ /**
172+ * Returns a list of regions enabled given the AWS account information
173+ * @param accountInfo aws account to check region
174+ * @returns Promise<string[]> a list of AWS regions enabled by the account
175+ */
176+ const getRegionsEnabled = async ( accountInfo : AWSAccountInfo ) : Promise < string [ ] > => {
177+ // Specify service region to avoid possible endpoint unavailable error
178+ const account = new Account ( { ...accountInfo , region : 'us-east-1' } ) ;
179+ const response = await account . listRegions ( ) . promise ( ) ;
180+ const enabledRegions = response . Regions . map ( r =>
181+ r . RegionOptStatus === 'ENABLED' || r . RegionOptStatus === 'ENABLED_BY_DEFAULT' ? r . RegionName : null ,
182+ ) . filter ( Boolean ) ;
183+
184+ return enabledRegions ;
185+ } ;
168186
169187/**
170188 * Returns a list of Amplify Apps in the region. The apps includes information about the CI build that created the app
@@ -173,8 +191,14 @@ const getOrphanTestIamRoles = async (account: AWSAccountInfo): Promise<IamRoleIn
173191 * @param region aws region to query for amplify Apps
174192 * @returns Promise<AmplifyAppInfo[]> a list of Amplify Apps in the region with build info
175193 */
176- const getAmplifyApps = async ( account : AWSAccountInfo , region : string ) : Promise < AmplifyAppInfo [ ] > => {
194+ const getAmplifyApps = async ( account : AWSAccountInfo , region : string , regionsEnabled : string [ ] ) : Promise < AmplifyAppInfo [ ] > => {
177195 const amplifyClient = new aws . Amplify ( getAWSConfig ( account , region ) ) ;
196+
197+ if ( ! regionsEnabled . includes ( region ) ) {
198+ console . error ( `Listing apps for account ${ account . accountId } -${ region } failed since ${ region } is not enabled. Skipping.` ) ;
199+ return [ ] ;
200+ }
201+
178202 const amplifyApps = await amplifyClient . listApps ( { maxResults : 50 } ) . promise ( ) ; // keeping it to 50 as max supported is 50
179203 const result : AmplifyAppInfo [ ] = [ ] ;
180204 for ( const app of amplifyApps . apps ) {
@@ -244,8 +268,14 @@ const getStackDetails = async (stackName: string, account: AWSAccountInfo, regio
244268 } ;
245269} ;
246270
247- const getStacks = async ( account : AWSAccountInfo , region : string ) : Promise < StackInfo [ ] > => {
271+ const getStacks = async ( account : AWSAccountInfo , region : string , regionsEnabled : string [ ] ) : Promise < StackInfo [ ] > => {
248272 const cfnClient = new aws . CloudFormation ( getAWSConfig ( account , region ) ) ;
273+
274+ if ( ! regionsEnabled . includes ( region ) ) {
275+ console . error ( `Listing stacks for account ${ account . accountId } -${ region } failed since ${ region } is not enabled. Skipping.` ) ;
276+ return [ ] ;
277+ }
278+
249279 const stacks = await cfnClient
250280 . listStacks ( {
251281 StackStatusFilter : [
@@ -725,8 +755,10 @@ const getAccountsToCleanup = async (): Promise<AWSAccountInfo[]> => {
725755} ;
726756
727757const cleanupAccount = async ( account : AWSAccountInfo , accountIndex : number , filterPredicate : JobFilterPredicate ) : Promise < void > => {
728- const appPromises = AWS_REGIONS_TO_RUN_TESTS . map ( region => getAmplifyApps ( account , region ) ) ;
729- const stackPromises = AWS_REGIONS_TO_RUN_TESTS . map ( region => getStacks ( account , region ) ) ;
758+ const regionsEnabled = await getRegionsEnabled ( account ) ;
759+
760+ const appPromises = AWS_REGIONS_TO_RUN_TESTS . map ( region => getAmplifyApps ( account , region , regionsEnabled ) ) ;
761+ const stackPromises = AWS_REGIONS_TO_RUN_TESTS . map ( region => getStacks ( account , region , regionsEnabled ) ) ;
730762 const bucketPromise = getS3Buckets ( account ) ;
731763 const orphanBucketPromise = getOrphanS3TestBuckets ( account ) ;
732764 const orphanIamRolesPromise = getOrphanTestIamRoles ( account ) ;
0 commit comments