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
48 changes: 33 additions & 15 deletions packages/amplify-e2e-core/src/utils/credentials-rotator.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { AssumeRoleCommand, STSClient } from '@aws-sdk/client-sts';
import { fromContainerMetadata } from '@aws-sdk/credential-providers';
import { generateRandomShortId, TEST_PROFILE_NAME } from './index';
import * as ini from 'ini';
import * as fs from 'fs-extra';
import { pathManager } from '@aws-amplify/amplify-cli-core';
const refreshCredentials = async (roleArn: string) => {
import { generateRandomShortId, TEST_PROFILE_NAME } from './index';

const refreshCredentials = async (roleArn: string, useCurrentCreds: boolean = false) => {
let credentials = undefined;
if (!useCurrentCreds) {
credentials = fromContainerMetadata();
}
const client = new STSClient({
// Use CodeBuild role to assume test account role. I.e. don't read credentials from process.env
credentials: fromContainerMetadata(),
credentials,
});
const sessionName = `testSession${generateRandomShortId()}`;
const command = new AssumeRoleCommand({
Expand All @@ -29,9 +33,15 @@ const refreshCredentials = async (roleArn: string) => {
await fs.writeFile(pathManager.getAWSCredentialsFilePath(), ini.stringify(credentialsContents));
};

const tryRefreshCredentials = async (roleArn: string) => {
/**
* Refresh the parent account. If child account is available, refresh that as well.
*/
const tryRefreshCredentials = async (parentRoleArn: string, childRoleArn?: string) => {
try {
await refreshCredentials(roleArn);
await refreshCredentials(parentRoleArn);
if (childRoleArn) {
await refreshCredentials(childRoleArn, true);
}
console.log('Test profile credentials refreshed');
} catch (e) {
console.error('Test profile credentials request failed');
Expand All @@ -54,16 +64,24 @@ export const tryScheduleCredentialRefresh = () => {
return;
}

if (!process.env.USE_PARENT_ACCOUNT) {
throw new Error('Credentials rotator supports only tests running in parent account at this time');
}
if (process.env.USE_PARENT_ACCOUNT) {
// Attempts to refresh credentials in background every 10 minutes.
setInterval(() => {
void tryRefreshCredentials(process.env.TEST_ACCOUNT_ROLE);
}, 10 * 60 * 1000);

// Attempts to refresh credentials in background every 15 minutes.
setInterval(() => {
void tryRefreshCredentials(process.env.TEST_ACCOUNT_ROLE);
}, 15 * 60 * 1000);
console.log('Test profile credentials refresh was scheduled for parent account');
return;
} else if (process.env.CHILD_ACCOUNT_ROLE) {
// Attempts to refresh credentials in background every 10 minutes.
setInterval(() => {
void tryRefreshCredentials(process.env.TEST_ACCOUNT_ROLE, process.env.CHILD_ACCOUNT_ROLE);
}, 10 * 60 * 1000);

isRotationBackgroundTaskAlreadyScheduled = true;
console.log('Test profile credentials refresh was scheduled for child account');
} else {
throw new Error('Credentials rotator could not find any role to rotate credentials for');
}

console.log('Test profile credentials refresh was scheduled');
isRotationBackgroundTaskAlreadyScheduled = true;
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
initJSProjectWithProfile,
setupRDSInstanceAndData,
sleep,
tryScheduleCredentialRefresh,
updateAuthAddUserGroups,
} from 'amplify-category-api-e2e-core';
import { existsSync, writeFileSync, removeSync } from 'fs-extra';
Expand Down Expand Up @@ -79,6 +80,7 @@ export const testOIDCFieldAuth = (engine: ImportedRDSType): void => {
console.log(sqlCreateStatements(engine));
projRoot = await createNewProjectDir(projName);
await setupAmplifyProject();
tryScheduleCredentialRefresh();
});

afterAll(async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as path from 'path';
import { createNewProjectDir, deleteProjectDir, deleteProject } from 'amplify-category-api-e2e-core';
import { createNewProjectDir, deleteProjectDir, deleteProject, tryScheduleCredentialRefresh } from 'amplify-category-api-e2e-core';
import { initCDKProject, cdkDeploy, cdkDestroy, createGen1ProjectForMigration, deleteDDBTables } from '../../commands';
import { graphql } from '../../graphql-request';
import { TestDefinition, writeStackConfig, writeTestDefinitions, writeOverrides } from '../../utils';
Expand All @@ -14,6 +14,10 @@ describe('Many-to-many Migration', () => {
let gen2ProjFolderName: string;
let dataSourceMapping: Record<string, string>;

beforeAll(() => {
tryScheduleCredentialRefresh();
});

beforeEach(async () => {
gen1ProjFolderName = 'mtmmigrationgen1';
gen2ProjFolderName = 'mtmmigrationgen2';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as path from 'path';
import { createNewProjectDir, deleteProjectDir, deleteProject } from 'amplify-category-api-e2e-core';
import { createNewProjectDir, deleteProjectDir, deleteProject, tryScheduleCredentialRefresh } from 'amplify-category-api-e2e-core';
import { CloudFormationClient, ListStacksCommand, DescribeStackEventsCommand, StackEvent } from '@aws-sdk/client-cloudformation';
import { initCDKProject, cdkDeploy, cdkDestroy, createGen1ProjectForMigration, deleteDDBTables } from '../../commands';
import { TestDefinition, writeStackConfig, writeTestDefinitions, writeOverrides } from '../../utils';
Expand All @@ -15,6 +15,7 @@ describe('Migration table import validation', () => {
let dataSourceMapping: Record<string, string>;

beforeAll(async () => {
tryScheduleCredentialRefresh();
gen1ProjFolderName = 'validategen1';
gen1ProjRoot = await createNewProjectDir(gen1ProjFolderName);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { TestOptions } from '../utils/sql-test-config-helper';
import { DURATION_1_HOUR } from '../utils/duration-constants';
import { testGraphQLAPI } from '../sql-tests-common/sql-models';
import { sqlCreateStatements } from '../sql-tests-common/tests-sources/sql-models/provider';
import { tryScheduleCredentialRefresh } from 'amplify-category-api-e2e-core';

jest.setTimeout(DURATION_1_HOUR);

Expand Down
4 changes: 3 additions & 1 deletion shared-scripts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -417,11 +417,13 @@ function useChildAccountCredentials {
echo "Unable to find a child account. Falling back to parent AWS account"
return
fi
creds=$(aws sts assume-role --role-arn arn:aws:iam::${pick_acct}:role/OrganizationAccountAccessRole --role-session-name testSession${session_id} --duration-seconds 3600)
account_role=arn:aws:iam::${pick_acct}:role/OrganizationAccountAccessRole
creds=$(aws sts assume-role --role-arn ${account_role} --role-session-name testSession${session_id} --duration-seconds 3600)
if [ -z $(echo $creds | jq -c -r '.AssumedRoleUser.Arn') ]; then
echo "Unable to assume child account role. Falling back to parent AWS account"
return
fi
export CHILD_ACCOUNT_ROLE=$account_role
export ORGANIZATION_SIZE=$org_size
export CREDS=$creds
echo "Using account credentials for $(echo $creds | jq -c -r '.AssumedRoleUser.Arn')"
Expand Down
Loading