Skip to content

Commit bb73c02

Browse files
chore: cdk deploy deps and syncing
1 parent 4438489 commit bb73c02

File tree

4 files changed

+236
-4
lines changed

4 files changed

+236
-4
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import {Construct} from "constructs"
2+
import {Duration, CustomResource} from "aws-cdk-lib"
3+
import {Function, Runtime, Code} from "aws-cdk-lib/aws-lambda"
4+
import {
5+
Role,
6+
ServicePrincipal,
7+
PolicyDocument,
8+
PolicyStatement,
9+
Effect
10+
} from "aws-cdk-lib/aws-iam"
11+
import {Provider} from "aws-cdk-lib/custom-resources"
12+
13+
export interface DelayResourceProps {
14+
/**
15+
* The delay time in seconds (default: 30 seconds)
16+
*/
17+
readonly delaySeconds?: number
18+
19+
/**
20+
* Optional description for the delay resource
21+
*/
22+
readonly description?: string
23+
}
24+
25+
/**
26+
* a fix for an annoying time sync issue that adds a configurable delay
27+
* to ensure AWS resources are fully available before dependent resources are created
28+
*/
29+
export class DelayResource extends Construct {
30+
public readonly customResource: CustomResource
31+
public readonly delaySeconds: number
32+
33+
constructor(scope: Construct, id: string, props: DelayResourceProps = {}) {
34+
super(scope, id)
35+
36+
this.delaySeconds = props.delaySeconds || 30
37+
38+
// create IAM role for the delay Lambda function
39+
const lambdaExecutionRole = new Role(this, "LambdaExecutionRole", {
40+
assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
41+
description: "Execution role for delay custom resource Lambda function",
42+
inlinePolicies: {
43+
LogsPolicy: new PolicyDocument({
44+
statements: [
45+
new PolicyStatement({
46+
effect: Effect.ALLOW,
47+
actions: [
48+
"logs:CreateLogGroup",
49+
"logs:CreateLogStream",
50+
"logs:PutLogEvents"
51+
],
52+
resources: ["*"]
53+
})
54+
]
55+
})
56+
}
57+
})
58+
59+
// create the delay Lambda function with inline Python code
60+
const delayFunction = new Function(this, "DelayFunction", {
61+
runtime: Runtime.PYTHON_3_12,
62+
handler: "index.handler",
63+
role: lambdaExecutionRole,
64+
timeout: Duration.minutes(15), // max Lambda timeout to handle long delays
65+
description: props.description || `Delay resource for ${this.delaySeconds} seconds`,
66+
code: Code.fromInline(`
67+
from time import sleep
68+
import json
69+
import cfnresponse
70+
import uuid
71+
72+
def handler(event, context):
73+
wait_seconds = 0
74+
id = str(uuid.uuid1())
75+
76+
print(f"Received event: {json.dumps(event, default=str)}")
77+
78+
try:
79+
if event["RequestType"] in ["Create", "Update"]:
80+
wait_seconds = int(event["ResourceProperties"].get("WaitSeconds", 0))
81+
print(f"Waiting for {wait_seconds} seconds...")
82+
sleep(wait_seconds)
83+
print(f"Wait complete")
84+
85+
response = {
86+
"TimeWaited": wait_seconds,
87+
"Id": id,
88+
"Status": "SUCCESS"
89+
}
90+
91+
cfnresponse.send(event, context, cfnresponse.SUCCESS, response, f"Waiter-{id}")
92+
93+
except Exception as e:
94+
print(f"Error: {str(e)}")
95+
cfnresponse.send(event, context, cfnresponse.FAILED, {"Error": str(e)}, f"Waiter-{id}")
96+
`)
97+
})
98+
99+
// create the custom resource provider
100+
const provider = new Provider(this, "DelayProvider", {
101+
onEventHandler: delayFunction,
102+
logRetention: 14 // 14 days log retention
103+
})
104+
105+
// create the custom resource that triggers the delay
106+
this.customResource = new CustomResource(this, "DelayCustomResource", {
107+
serviceToken: provider.serviceToken,
108+
properties: {
109+
WaitSeconds: this.delaySeconds,
110+
Description: props.description || `Delay for ${this.delaySeconds} seconds`,
111+
// timestamp to ensure updates trigger when properties change
112+
Timestamp: Date.now()
113+
}
114+
})
115+
}
116+
117+
}

packages/cdk/nagSuppressions.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable max-len */
12

23
import {Stack} from "aws-cdk-lib"
34
import {NagPackSuppression, NagSuppressions} from "cdk-nag"
@@ -187,6 +188,97 @@ export const nagSuppressions = (stack: Stack) => {
187188
)
188189
})
189190

191+
// Suppress DelayResource IAM and runtime issues
192+
safeAddNagSuppression(
193+
stack,
194+
"/EpsAssistMeStack/VectorIndex/PolicySyncWait/LambdaExecutionRole/Resource",
195+
[
196+
{
197+
id: "AwsSolutions-IAM5",
198+
reason: "DelayResource Lambda requires wildcard log permissions for CloudWatch logging."
199+
}
200+
]
201+
)
202+
203+
safeAddNagSuppression(
204+
stack,
205+
"/EpsAssistMeStack/VectorIndex/IndexReadyWait/LambdaExecutionRole/Resource",
206+
[
207+
{
208+
id: "AwsSolutions-IAM5",
209+
reason: "DelayResource Lambda requires wildcard log permissions for CloudWatch logging."
210+
}
211+
]
212+
)
213+
214+
// Suppress DelayProvider framework ServiceRole issues
215+
safeAddNagSuppression(
216+
stack,
217+
"/EpsAssistMeStack/VectorIndex/PolicySyncWait/DelayProvider/framework-onEvent/ServiceRole/Resource",
218+
[
219+
{
220+
id: "AwsSolutions-IAM4",
221+
reason: "Auto-generated CDK Provider role uses AWS managed policy for Lambda execution."
222+
}
223+
]
224+
)
225+
226+
safeAddNagSuppression(
227+
stack,
228+
"/EpsAssistMeStack/VectorIndex/PolicySyncWait/DelayProvider/framework-onEvent/ServiceRole/DefaultPolicy/Resource",
229+
[
230+
{
231+
id: "AwsSolutions-IAM5",
232+
reason: "Auto-generated CDK Provider role requires wildcard permissions for Lambda invocation."
233+
}
234+
]
235+
)
236+
237+
safeAddNagSuppression(
238+
stack,
239+
"/EpsAssistMeStack/VectorIndex/IndexReadyWait/DelayProvider/framework-onEvent/ServiceRole/Resource",
240+
[
241+
{
242+
id: "AwsSolutions-IAM4",
243+
reason: "Auto-generated CDK Provider role uses AWS managed policy for Lambda execution."
244+
}
245+
]
246+
)
247+
248+
safeAddNagSuppression(
249+
stack,
250+
"/EpsAssistMeStack/VectorIndex/IndexReadyWait/DelayProvider/framework-onEvent/ServiceRole/DefaultPolicy/Resource",
251+
[
252+
{
253+
id: "AwsSolutions-IAM5",
254+
reason: "Auto-generated CDK Provider role requires wildcard permissions for Lambda invocation."
255+
}
256+
]
257+
)
258+
259+
// Suppress DelayFunction runtime version warnings
260+
safeAddNagSuppression(
261+
stack,
262+
"/EpsAssistMeStack/VectorIndex/PolicySyncWait/DelayFunction/Resource",
263+
[
264+
{
265+
id: "AwsSolutions-L1",
266+
reason: "DelayResource uses Python 3.12 which is the latest stable runtime available for the delay functionality."
267+
}
268+
]
269+
)
270+
271+
safeAddNagSuppression(
272+
stack,
273+
"/EpsAssistMeStack/VectorIndex/IndexReadyWait/DelayFunction/Resource",
274+
[
275+
{
276+
id: "AwsSolutions-L1",
277+
reason: "DelayResource uses Python 3.12 which is the latest stable runtime available for the delay functionality."
278+
}
279+
]
280+
)
281+
190282
}
191283

192284
const safeAddNagSuppression = (stack: Stack, path: string, suppressions: Array<NagPackSuppression>) => {

packages/cdk/resources/VectorIndex.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {Construct} from "constructs"
22
import {CfnIndex} from "aws-cdk-lib/aws-opensearchserverless"
33
import {VectorCollection} from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/opensearchserverless"
44
import {RemovalPolicy} from "aws-cdk-lib"
5+
import {DelayResource} from "../constructs/DelayResource"
56

67
export interface VectorIndexProps {
78
readonly stackName: string
@@ -11,6 +12,8 @@ export interface VectorIndexProps {
1112
export class VectorIndex extends Construct {
1213
public readonly cfnIndex: CfnIndex
1314
public readonly indexName: string
15+
public readonly policySyncWait: DelayResource
16+
public readonly indexReadyWait: DelayResource
1417

1518
constructor(scope: Construct, id: string, props: VectorIndexProps) {
1619
super(scope, id)
@@ -64,9 +67,31 @@ export class VectorIndex extends Construct {
6467
}
6568
})
6669

67-
cfnIndex.node.addDependency(props.collection.dataAccessPolicy)
70+
// a fix for an annoying time sync issue that adds a small delay
71+
// to ensure data access policies are synced before index creation
72+
const policySyncWait = new DelayResource(this, "PolicySyncWait", {
73+
delaySeconds: 15,
74+
description: "Wait for OpenSearch data access policies to sync"
75+
})
76+
77+
policySyncWait.customResource.node.addDependency(props.collection.dataAccessPolicy)
78+
79+
// Index depends on policy sync wait instead of directly on dataAccessPolicy
80+
cfnIndex.node.addDependency(policySyncWait.customResource)
6881

6982
cfnIndex.applyRemovalPolicy(RemovalPolicy.DESTROY)
83+
84+
// a fix for an annoying time sync issue that adds a small delay
85+
// to ensure index is actually available for Bedrock
86+
const indexReadyWait = new DelayResource(this, "IndexReadyWait", {
87+
delaySeconds: 30,
88+
description: "Wait for OpenSearch index to be fully available"
89+
})
90+
91+
indexReadyWait.customResource.node.addDependency(cfnIndex)
92+
7093
this.cfnIndex = cfnIndex
94+
this.policySyncWait = policySyncWait
95+
this.indexReadyWait = indexReadyWait
7196
}
7297
}

packages/cdk/stacks/EpsAssistMeStack.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,7 @@ export class EpsAssistMeStack extends Stack {
105105
account
106106
})
107107

108-
vectorKB.knowledgeBase.node.addDependency(vectorIndex.cfnIndex)
109-
vectorKB.knowledgeBase.node.addDependency(openSearchResources.collection)
110-
vectorKB.knowledgeBase.node.addDependency(openSearchResources.collection.dataAccessPolicy)
108+
vectorKB.knowledgeBase.node.addDependency(vectorIndex.indexReadyWait.customResource)
111109

112110
// Create runtime policies with resource dependencies
113111
const runtimePolicies = new RuntimePolicies(this, "RuntimePolicies", {

0 commit comments

Comments
 (0)