forked from aws/agentcore-cli
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathstack-status.ts
More file actions
113 lines (102 loc) · 3.57 KB
/
Copy pathstack-status.ts
File metadata and controls
113 lines (102 loc) · 3.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { getCredentialProvider } from '../aws';
import { CloudFormationClient, DescribeStacksCommand } from '@aws-sdk/client-cloudformation';
/**
* CloudFormation stack statuses that indicate the stack is in a transitional state
* and cannot be updated until the current operation completes.
*/
const IN_PROGRESS_STATUSES = new Set([
'CREATE_IN_PROGRESS',
'UPDATE_IN_PROGRESS',
'DELETE_IN_PROGRESS',
'ROLLBACK_IN_PROGRESS',
'UPDATE_ROLLBACK_IN_PROGRESS',
'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS',
'DELETE_COMPLETE_CLEANUP_IN_PROGRESS',
'REVIEW_IN_PROGRESS',
'IMPORT_IN_PROGRESS',
'IMPORT_ROLLBACK_IN_PROGRESS',
]);
/**
* CloudFormation stack statuses that indicate the stack is in a failed state
* and may require manual intervention.
*/
const FAILED_STATUSES = new Set([
'CREATE_FAILED',
'ROLLBACK_FAILED',
'DELETE_FAILED',
'UPDATE_ROLLBACK_FAILED',
'IMPORT_ROLLBACK_FAILED',
]);
export interface StackStatusResult {
/** Whether the stack can be deployed to (is in a stable, deployable state) */
canDeploy: boolean;
/** Whether the stack exists */
exists: boolean;
/** The current stack status, if the stack exists */
status?: string;
/** User-friendly message explaining why deployment is blocked */
message?: string;
}
/**
* Check if a CloudFormation stack is in a deployable state.
*
* Returns information about whether the stack can be deployed to:
* - If the stack doesn't exist, deployment can proceed (will create)
* - If the stack is in a stable state (*_COMPLETE), deployment can proceed
* - If the stack is in progress, deployment must wait
* - If the stack is in a failed state, deployment may require manual intervention
*/
export async function checkStackStatus(region: string, stackName: string): Promise<StackStatusResult> {
const cfn = new CloudFormationClient({ region, credentials: getCredentialProvider() });
try {
const resp = await cfn.send(new DescribeStacksCommand({ StackName: stackName }));
const stack = resp.Stacks?.[0];
if (!stack?.StackStatus) {
// Stack exists but no status - shouldn't happen, but treat as deployable
return { canDeploy: true, exists: true };
}
const status = stack.StackStatus;
// Check if stack is in a transitional state
if (IN_PROGRESS_STATUSES.has(status)) {
return {
canDeploy: false,
exists: true,
status,
message: `Stack "${stackName}" is currently in ${status} state. Please wait for the operation to complete before deploying.`,
};
}
// Check if stack is in a failed state
if (FAILED_STATUSES.has(status)) {
return {
canDeploy: false,
exists: true,
status,
message: `Stack "${stackName}" is in ${status} state. Manual intervention may be required before deploying.`,
};
}
// Stack is in a stable state - can deploy
return { canDeploy: true, exists: true, status };
} catch (err: unknown) {
// Stack doesn't exist - safe to deploy (will create new stack)
if (err instanceof Error && err.name === 'ValidationError') {
return { canDeploy: true, exists: false };
}
throw err;
}
}
/**
* Check multiple stacks and return the first one that blocks deployment.
* Returns null if all stacks are deployable.
*/
export async function checkStacksStatus(
region: string,
stackNames: string[]
): Promise<{ stackName: string; result: StackStatusResult } | null> {
for (const stackName of stackNames) {
const result = await checkStackStatus(region, stackName);
if (!result.canDeploy) {
return { stackName, result };
}
}
return null;
}