Skip to content

Commit c671368

Browse files
committed
feat: auth and storage
1 parent 67f3e7a commit c671368

26 files changed

Lines changed: 5619 additions & 17 deletions

packages/amplify-cli/package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@
6969
"@aws-sdk/client-amplify": "^3.624.0",
7070
"@aws-sdk/client-cloudformation": "^3.624.0",
7171
"@aws-sdk/client-cognito-identity-provider": "^3.624.0",
72+
"@aws-sdk/client-ssm": "^3.624.0",
73+
"@aws-sdk/client-sts": "^3.624.0",
7274
"amplify-codegen": "^4.10.3",
7375
"amplify-dotnet-function-runtime-provider": "2.1.6",
7476
"amplify-go-function-runtime-provider": "2.3.53",
@@ -111,6 +113,7 @@
111113
"@aws-sdk/client-cognito-identity-provider": "^3.624.0",
112114
"@aws-sdk/client-lambda": "^3.624.0",
113115
"@aws-sdk/client-s3": "^3.624.0",
116+
"@jest/globals": "^29.7.0",
114117
"@types/archiver": "^5.3.1",
115118
"@types/columnify": "^1.5.1",
116119
"@types/folder-hash": "^4.0.1",
@@ -151,7 +154,10 @@
151154
"json",
152155
"node"
153156
],
154-
"collectCoverage": true
157+
"collectCoverage": true,
158+
"setupFilesAfterEnv": [
159+
"<rootDir>/src/commands/gen2-migration/refactor/generators/setup-jest.ts"
160+
]
155161
},
156162
"amplify": {
157163
"officialPlugins": {

packages/amplify-cli/src/commands/gen2-migration/refactor.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import {
2+
CloudFormationClient,
3+
CreateStackRefactorCommand,
4+
CreateStackRefactorCommandInput,
5+
DescribeStackRefactorCommand,
6+
DescribeStackRefactorCommandOutput,
7+
ExecuteStackRefactorCommand,
8+
StackRefactorExecutionStatus,
9+
StackRefactorStatus,
10+
} from '@aws-sdk/client-cloudformation';
11+
import assert from 'node:assert';
12+
import { CFNStackStatus, FailedRefactorResponse } from './types';
13+
import { pollStackForCompletionState } from './cfn-stack-updater';
14+
15+
const POLL_ATTEMPTS = 300;
16+
const POLL_INTERVAL_MS = 12000;
17+
const COMPLETION_STATE = '_COMPLETE';
18+
const FAILED_STATE = '_FAILED';
19+
export const UPDATE_COMPLETE = 'UPDATE_COMPLETE';
20+
/**
21+
* Refactors a stack with given source and destination template.
22+
* @param cfnClient
23+
* @param createStackRefactorCommandInput
24+
* @param attempts number of attempts to poll CFN stack for update completion state. The interval between the polls is 1.5 seconds.
25+
* @returns a tuple containing the success/failed state and the reason if any.
26+
*/
27+
export async function tryRefactorStack(
28+
cfnClient: CloudFormationClient,
29+
createStackRefactorCommandInput: CreateStackRefactorCommandInput,
30+
attempts = POLL_ATTEMPTS,
31+
): Promise<[boolean, FailedRefactorResponse | undefined]> {
32+
const { StackRefactorId } = await cfnClient.send(new CreateStackRefactorCommand(createStackRefactorCommandInput));
33+
assert(StackRefactorId);
34+
let describeStackRefactorResponse = await pollStackRefactorForCompletionState(
35+
cfnClient,
36+
StackRefactorId,
37+
(_describeStackRefactorResponse: DescribeStackRefactorCommandOutput) => {
38+
assert(_describeStackRefactorResponse.Status);
39+
return (
40+
_describeStackRefactorResponse.Status.endsWith(COMPLETION_STATE) || _describeStackRefactorResponse.Status.endsWith(FAILED_STATE)
41+
);
42+
},
43+
attempts,
44+
);
45+
if (describeStackRefactorResponse.Status !== StackRefactorStatus.CREATE_COMPLETE) {
46+
return [
47+
false,
48+
{
49+
status: describeStackRefactorResponse.Status,
50+
reason: describeStackRefactorResponse.StatusReason,
51+
stackRefactorId: StackRefactorId,
52+
},
53+
];
54+
}
55+
await cfnClient.send(
56+
new ExecuteStackRefactorCommand({
57+
StackRefactorId,
58+
}),
59+
);
60+
describeStackRefactorResponse = await pollStackRefactorForCompletionState(
61+
cfnClient,
62+
StackRefactorId,
63+
(describeStackRefactorResponse: DescribeStackRefactorCommandOutput) => {
64+
assert(describeStackRefactorResponse.ExecutionStatus);
65+
return (
66+
describeStackRefactorResponse.ExecutionStatus.endsWith(COMPLETION_STATE) ||
67+
describeStackRefactorResponse.ExecutionStatus.endsWith(FAILED_STATE)
68+
);
69+
},
70+
attempts,
71+
);
72+
if (describeStackRefactorResponse.ExecutionStatus !== StackRefactorExecutionStatus.EXECUTE_COMPLETE) {
73+
return [
74+
false,
75+
{
76+
status: describeStackRefactorResponse.ExecutionStatus,
77+
stackRefactorId: StackRefactorId,
78+
reason: describeStackRefactorResponse.ExecutionStatusReason,
79+
},
80+
];
81+
}
82+
83+
const sourceStackName = createStackRefactorCommandInput.StackDefinitions?.[0].StackName;
84+
const destinationStackName = createStackRefactorCommandInput.StackDefinitions?.[1].StackName;
85+
assert(sourceStackName);
86+
assert(destinationStackName);
87+
const sourceStackStatus = await pollStackForCompletionState(cfnClient, sourceStackName);
88+
assert(sourceStackStatus === CFNStackStatus.UPDATE_COMPLETE, `${sourceStackName} was not updated successfully.`);
89+
const destinationStackStatus = await pollStackForCompletionState(cfnClient, destinationStackName);
90+
assert(destinationStackStatus === CFNStackStatus.UPDATE_COMPLETE, `${destinationStackName} was not updated successfully.`);
91+
92+
return [true, undefined];
93+
}
94+
95+
/**
96+
* Polls a stack refactor operation for completion state
97+
* @param cfnClient
98+
* @param stackRefactorId
99+
* @param exitCondition a function that determines if the stack refactor operation has reached a completion state.
100+
* @param attempts number of attempts to poll for completion.
101+
* @returns the stack status
102+
*/
103+
async function pollStackRefactorForCompletionState(
104+
cfnClient: CloudFormationClient,
105+
stackRefactorId: string,
106+
exitCondition: (describeStackRefactorResponse: DescribeStackRefactorCommandOutput) => boolean,
107+
attempts: number,
108+
): Promise<DescribeStackRefactorCommandOutput> {
109+
do {
110+
const describeStackRefactorResponse = await cfnClient.send(
111+
new DescribeStackRefactorCommand({
112+
StackRefactorId: stackRefactorId,
113+
}),
114+
);
115+
if (exitCondition(describeStackRefactorResponse)) {
116+
return describeStackRefactorResponse;
117+
}
118+
await new Promise((res) => setTimeout(() => res(''), POLL_INTERVAL_MS));
119+
attempts--;
120+
} while (attempts > 0);
121+
throw new Error(`Stack refactor ${stackRefactorId} did not reach a completion state within the given time period.`);
122+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { CloudFormationClient, DescribeStacksCommand, Parameter, UpdateStackCommand } from '@aws-sdk/client-cloudformation';
2+
import { CFNTemplate } from './types';
3+
import assert from 'node:assert';
4+
5+
const POLL_ATTEMPTS = 60;
6+
const POLL_INTERVAL_MS = 1500;
7+
const NO_UPDATES_MESSAGE = 'No updates are to be performed';
8+
const CFN_IAM_CAPABILIY = 'CAPABILITY_NAMED_IAM';
9+
const COMPLETION_STATE = '_COMPLETE';
10+
export const UPDATE_COMPLETE = 'UPDATE_COMPLETE';
11+
/**
12+
* Updates a stack with given template. If no updates are present, it no-ops.
13+
* @param cfnClient
14+
* @param stackName
15+
* @param parameters
16+
* @param templateBody
17+
* @param attempts number of attempts to poll CFN stack for update completion state. The interval between the polls is 1.5 seconds.
18+
*/
19+
export async function tryUpdateStack(
20+
cfnClient: CloudFormationClient,
21+
stackName: string,
22+
parameters: Parameter[],
23+
templateBody: CFNTemplate,
24+
attempts = POLL_ATTEMPTS,
25+
): Promise<string> {
26+
try {
27+
await cfnClient.send(
28+
new UpdateStackCommand({
29+
TemplateBody: JSON.stringify(templateBody),
30+
Parameters: parameters,
31+
StackName: stackName,
32+
Capabilities: [CFN_IAM_CAPABILIY],
33+
Tags: [],
34+
}),
35+
);
36+
return pollStackForCompletionState(cfnClient, stackName, attempts);
37+
} catch (e) {
38+
if (!e.message.includes(NO_UPDATES_MESSAGE)) {
39+
throw e;
40+
}
41+
return UPDATE_COMPLETE;
42+
}
43+
}
44+
45+
/**
46+
* Polls a stack for completion state
47+
* @param cfnClient
48+
* @param stackName
49+
* @param attempts number of attempts to poll for completion.
50+
* @returns the stack status
51+
*/
52+
export async function pollStackForCompletionState(
53+
cfnClient: CloudFormationClient,
54+
stackName: string,
55+
attempts: number = POLL_ATTEMPTS,
56+
): Promise<string> {
57+
do {
58+
const { Stacks } = await cfnClient.send(
59+
new DescribeStacksCommand({
60+
StackName: stackName,
61+
}),
62+
);
63+
const stack = Stacks?.[0];
64+
assert(stack);
65+
const stackStatus = stack.StackStatus;
66+
assert(stackStatus);
67+
if (stackStatus?.endsWith(COMPLETION_STATE)) {
68+
return stackStatus;
69+
}
70+
await new Promise((res) => setTimeout(() => res(''), POLL_INTERVAL_MS));
71+
attempts--;
72+
} while (attempts > 0);
73+
throw new Error(`Stack ${stackName} did not reach a completion state within the given time period.`);
74+
}

0 commit comments

Comments
 (0)