Skip to content

Commit 6370be4

Browse files
author
Yuriy Bezsonov
committed
WIP
1 parent 4d4fc68 commit 6370be4

File tree

3 files changed

+135
-25
lines changed

3 files changed

+135
-25
lines changed

infra/cdk/src/main/java/sample/com/WorkshopStack.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
5454
Ide ide = new Ide(this, "Ide", IdeProps.builder()
5555
.vpc(vpc.getVpc())
5656
.gitBranch(gitBranch)
57+
.templateType(templateType)
5758
.build());
5859

5960
// Custom roles only for non-base templates

infra/cdk/src/main/java/sample/com/constructs/Ide.java

Lines changed: 117 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public static class IdeProps {
5656
"ms-azuretools.vscode-docker"
5757
);
5858
private String gitBranch = "main";
59+
private String templateType = "base";
5960

6061
public static IdeProps.Builder builder() { return new Builder(); }
6162

@@ -71,6 +72,7 @@ public static class Builder {
7172
public Builder bootstrapTimeoutMinutes(int bootstrapTimeoutMinutes) { props.bootstrapTimeoutMinutes = bootstrapTimeoutMinutes; return this; }
7273
public Builder vscodeExtensions(List<String> vscodeExtensions) { props.vscodeExtensions = vscodeExtensions; return this; }
7374
public Builder gitBranch(String gitBranch) { props.gitBranch = gitBranch; return this; }
75+
public Builder templateType(String templateType) { props.templateType = templateType; return this; }
7476

7577
public IdeProps build() { return props; }
7678
}
@@ -85,6 +87,7 @@ public static class Builder {
8587
public int getBootstrapTimeoutMinutes() { return bootstrapTimeoutMinutes; }
8688
public List<String> getVscodeExtensions() { return vscodeExtensions; }
8789
public String getGitBranch() { return gitBranch; }
90+
public String getTemplateType() { return templateType; }
8891
}
8992

9093
public Ide(final Construct scope, final String id, final IVpc vpc) {
@@ -241,14 +244,120 @@ public Ide(final Construct scope, final String id, final IdeProps props) {
241244
// Create User Data for bootstrap with CloudWatch logging
242245
var userData = UserData.forLinux();
243246
String extensionsString = String.join(",", props.getVscodeExtensions());
244-
String bootstrapScript = loadFile("/ec2-userdata.sh")
245-
.replace("${vscodeExtensions}", extensionsString)
246-
.replace("${templateType}", "base")
247-
.replace("${gitBranch}", props.getGitBranch())
248-
.replace("${stackName}", Aws.STACK_NAME)
249-
.replace("${awsRegion}", Aws.REGION)
250-
.replace("${idePassword}", ideSecretsManagerPassword.secretValueFromJson("password").unsafeUnwrap());
251-
userData.addCommands(bootstrapScript.split("\n"));
247+
String gitBranch = props.getGitBranch();
248+
String templateType = props.getTemplateType();
249+
250+
// Build UserData content with proper substitutions
251+
String userDataContent = String.format("""
252+
#!/bin/bash
253+
set -e
254+
255+
# Minimal EC2 UserData script - downloads and runs full bootstrap
256+
# This keeps UserData under size limits while allowing unlimited bootstrap size
257+
258+
# Configuration from CDK
259+
GIT_BRANCH="%s"
260+
IDE_PASSWORD="%s"
261+
STACK_NAME="%s"
262+
AWS_REGION="%s"
263+
TEMPLATE_TYPE="%s"
264+
VSCODE_EXTENSIONS="%s"
265+
266+
# Setup logging
267+
LOG_GROUP_NAME="ide-bootstrap-$(date +%%Y%%m%%d-%%H%%M%%S)"
268+
echo "Bootstrap logs will be written to CloudWatch log group: $LOG_GROUP_NAME"
269+
270+
# Install CloudWatch agent for logging
271+
dnf install -y amazon-cloudwatch-agent
272+
273+
# Create CloudWatch agent configuration
274+
cat > /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json << EOF
275+
{
276+
"logs": {
277+
"logs_collected": {
278+
"files": {
279+
"collect_list": [
280+
{
281+
"file_path": "/var/log/bootstrap.log",
282+
"log_group_name": "$LOG_GROUP_NAME",
283+
"log_stream_name": "{instance_id}",
284+
"retention_in_days": 7
285+
}
286+
]
287+
}
288+
}
289+
}
290+
}
291+
EOF
292+
293+
# Start CloudWatch agent
294+
/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \\
295+
-a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json -s
296+
297+
# Redirect all output to log file and console
298+
exec > >(tee -a /var/log/bootstrap.log)
299+
exec 2>&1
300+
301+
echo "UserData started at $(date) - Logging to $LOG_GROUP_NAME"
302+
303+
# Download and run full bootstrap script with retry logic
304+
download_bootstrap() {
305+
local urls=(
306+
"https://raw.githubusercontent.com/aws-samples/java-on-aws/${GIT_BRANCH}/infra/scripts/ide/bootstrap.sh"
307+
"https://github.com/aws-samples/java-on-aws/raw/${GIT_BRANCH}/infra/scripts/ide/bootstrap.sh"
308+
)
309+
local max_attempts=5
310+
local delay=5
311+
312+
for attempt in $(seq 1 $max_attempts); do
313+
echo "Download attempt $attempt of $max_attempts"
314+
315+
for url in "${urls[@]}"; do
316+
echo "Trying to download bootstrap from: $url"
317+
if curl -fsSL --connect-timeout 30 --max-time 60 "$url" -o /tmp/bootstrap.sh; then
318+
echo "Successfully downloaded bootstrap script on attempt $attempt"
319+
return 0
320+
fi
321+
echo "Failed to download from: $url"
322+
done
323+
324+
if [ $attempt -lt $max_attempts ]; then
325+
echo "All URLs failed on attempt $attempt, waiting ${delay}s before retry..."
326+
sleep $delay
327+
fi
328+
done
329+
330+
echo "All download attempts failed after $max_attempts tries"
331+
return 1
332+
}
333+
334+
if download_bootstrap; then
335+
chmod +x /tmp/bootstrap.sh
336+
echo "Executing full bootstrap script..."
337+
export VSCODE_EXTENSIONS="$VSCODE_EXTENSIONS"
338+
if /tmp/bootstrap.sh "$IDE_PASSWORD" "$GIT_BRANCH" "$STACK_NAME" "$AWS_REGION" "$TEMPLATE_TYPE"; then
339+
echo "Bootstrap completed successfully"
340+
/opt/aws/bin/cfn-signal -e 0 --stack "$STACK_NAME" --resource IdeBootstrapWaitCondition --region "$AWS_REGION"
341+
else
342+
echo "FATAL: Bootstrap script failed"
343+
/opt/aws/bin/cfn-signal -e 1 --stack "$STACK_NAME" --resource IdeBootstrapWaitCondition --region "$AWS_REGION"
344+
exit 1
345+
fi
346+
else
347+
echo "FATAL: Could not download bootstrap script from any source"
348+
/opt/aws/bin/cfn-signal -e 1 --stack "$STACK_NAME" --resource IdeBootstrapWaitCondition --region "$AWS_REGION"
349+
exit 1
350+
fi
351+
""",
352+
gitBranch,
353+
ideSecretsManagerPassword.secretValueFromJson("password").unsafeUnwrap(),
354+
Aws.STACK_NAME,
355+
Aws.REGION,
356+
templateType,
357+
extensionsString
358+
);
359+
360+
userData.addCommands(userDataContent);
252361

253362
// Create instance launcher Lambda with multi-AZ and multi-instance-type failover
254363
var instanceLauncher = new Lambda(this, "InstanceLauncher",

infra/workshop-template.yaml

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,11 @@ Resources:
741741
Fn::GetAtt:
742742
- IdeInstanceLauncherFunction803C5A2A
743743
- Arn
744+
IamInstanceProfileArn:
745+
Fn::GetAtt:
746+
- IdeIdeInstanceProfile8BD997EA
747+
- Arn
748+
VolumeSize: "50"
744749
SubnetIds:
745750
Fn::Join:
746751
- ""
@@ -772,7 +777,7 @@ Resources:
772777
# This keeps UserData under size limits while allowing unlimited bootstrap size
773778

774779
# Configuration from CDK
775-
GIT_BRANCH="${gitBranch:-main}"
780+
GIT_BRANCH="new-ws-infra"
776781
IDE_PASSWORD="{{resolve:secretsmanager:
777782
- Ref: IdeIdePasswordSecretF3482811
778783
- |-
@@ -783,10 +788,10 @@ Resources:
783788
"
784789
AWS_REGION="
785790
- Ref: AWS::Region
786-
- |-
791+
- |
787792
"
788-
TEMPLATE_TYPE="${templateType:-base}"
789-
VSCODE_EXTENSIONS="${vscodeExtensions:-}"
793+
TEMPLATE_TYPE="base"
794+
VSCODE_EXTENSIONS="shardulm94.trailing-spaces,ms-kubernetes-tools.vscode-kubernetes-tools,ms-azuretools.vscode-docker"
790795
791796
# Setup logging
792797
LOG_GROUP_NAME="ide-bootstrap-$(date +%Y%m%d-%H%M%S)"
@@ -875,11 +880,6 @@ Resources:
875880
fi
876881
InstanceTypes: m5.xlarge,m6i.xlarge,t3.xlarge
877882
InstanceName: ide
878-
IamInstanceProfileArn:
879-
Fn::GetAtt:
880-
- IdeIdeInstanceProfile8BD997EA
881-
- Arn
882-
VolumeSize: "50"
883883
UpdateReplacePolicy: Delete
884884
DeletionPolicy: Delete
885885
IdeIdeEipAssociation6C6C215D:
@@ -1128,12 +1128,12 @@ Resources:
11281128
Environment:
11291129
ComputeType: BUILD_GENERAL1_MEDIUM
11301130
EnvironmentVariables:
1131-
- Name: GIT_BRANCH
1132-
Type: PLAINTEXT
1133-
Value: new-ws-infra
11341131
- Name: TEMPLATE_TYPE
11351132
Type: PLAINTEXT
11361133
Value: base
1134+
- Name: GIT_BRANCH
1135+
Type: PLAINTEXT
1136+
Value: new-ws-infra
11371137
- Name: STACK_NAME
11381138
Type: PLAINTEXT
11391139
Value:
@@ -1374,12 +1374,12 @@ Resources:
13741374
Description: base-setup build complete
13751375
EventPattern:
13761376
detail:
1377+
project-name:
1378+
- Ref: CodeBuildProjectA0FF5539
13771379
build-status:
13781380
- SUCCEEDED
13791381
- FAILED
13801382
- STOPPED
1381-
project-name:
1382-
- Ref: CodeBuildProjectA0FF5539
13831383
detail-type:
13841384
- CodeBuild Build State Change
13851385
source:
@@ -1411,13 +1411,13 @@ Resources:
14111411
Fn::GetAtt:
14121412
- CodeBuildStartLambdaFunction8349284F
14131413
- Arn
1414+
ProjectName:
1415+
Ref: CodeBuildProjectA0FF5539
14141416
CodeBuildIamRoleArn:
14151417
Fn::GetAtt:
14161418
- CodeBuildCodeBuildRoleBA9C6D5C
14171419
- Arn
1418-
ContentHash: "1765651951549"
1419-
ProjectName:
1420-
Ref: CodeBuildProjectA0FF5539
1420+
ContentHash: "1765653430116"
14211421
DependsOn:
14221422
- CodeBuildBuildCompleteRuleAllowEventRuleWorkshopStackCodeBuildReportLambdaFunctionD77C6091DA4A4BD8
14231423
- CodeBuildBuildCompleteRule06AAF17D

0 commit comments

Comments
 (0)