Skip to content

Commit 977b4af

Browse files
author
Yuriy Bezsonov
committed
feat(java-on-aws-infra): enhance ECR login resilience and add deployment dependencies
1 parent 21c06a9 commit 977b4af

5 files changed

Lines changed: 87 additions & 14 deletions

File tree

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

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
9090
.build());
9191

9292
// ECR Registry settings (Repository Creation Template for create-on-push)
93-
new EcrRegistry(this, "EcrRegistry",
93+
EcrRegistry ecrRegistry = new EcrRegistry(this, "EcrRegistry",
9494
EcrRegistry.EcrRegistryProps.builder()
9595
.prefix(prefix)
9696
.build());
@@ -247,8 +247,23 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
247247
build:
248248
commands:
249249
- |
250+
set -e
250251
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
251-
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
252+
ECR_REGISTRY=$ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
253+
254+
# ECR login with retry (network can be slow on first attempt)
255+
for i in 1 2 3; do
256+
echo "ECR login attempt $i..."
257+
if aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REGISTRY; then
258+
echo "ECR login successful"
259+
break
260+
fi
261+
if [ $i -eq 3 ]; then
262+
echo "ECR login failed after 3 attempts"
263+
exit 1
264+
fi
265+
sleep 10
266+
done
252267
253268
# Create placeholder Dockerfile
254269
cat > /tmp/Dockerfile << 'EOF'
@@ -261,8 +276,8 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
261276
262277
# Build and push placeholder image (create-on-push creates repo)
263278
docker build -t placeholder /tmp
264-
docker tag placeholder $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/aiagent:latest
265-
docker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/aiagent:latest
279+
docker tag placeholder $ECR_REGISTRY/aiagent:latest
280+
docker push $ECR_REGISTRY/aiagent:latest
266281
""";
267282

268283
CodeBuild placeholderImageBuild = new CodeBuild(this, "PlaceholderImageBuild",
@@ -273,6 +288,10 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
273288
.environmentVariables(Map.of(
274289
"TEMPLATE_TYPE", templateType))
275290
.buildSpec(placeholderBuildSpec)
291+
.dependencies(java.util.List.of(
292+
vpc.getConcreteVpc(), // Ensures NAT Gateway is ready
293+
ecrRegistry.getRepositoryCreationTemplate() // Ensures ECR create-on-push is configured
294+
))
276295
.build());
277296

278297
// ECS Express Service for AI Agent

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public static class CodeBuildProps {
3232
private IVpc vpc;
3333
private Map<String, String> environmentVariables;
3434
private String buildSpec;
35+
private List<software.constructs.IDependable> dependencies;
3536

3637
public static CodeBuildProps.Builder builder() { return new Builder(); }
3738

@@ -46,6 +47,7 @@ public static class Builder {
4647
public Builder vpc(IVpc vpc) { props.vpc = vpc; return this; }
4748
public Builder environmentVariables(Map<String, String> environmentVariables) { props.environmentVariables = environmentVariables; return this; }
4849
public Builder buildSpec(String buildSpec) { props.buildSpec = buildSpec; return this; }
50+
public Builder dependencies(List<software.constructs.IDependable> dependencies) { props.dependencies = dependencies; return this; }
4951

5052
public CodeBuildProps build() { return props; }
5153
}
@@ -59,6 +61,7 @@ public static class Builder {
5961
public IVpc getVpc() { return vpc; }
6062
public Map<String, String> getEnvironmentVariables() { return environmentVariables; }
6163
public String getBuildSpec() { return buildSpec; }
64+
public List<software.constructs.IDependable> getDependencies() { return dependencies; }
6265
}
6366

6467
public CodeBuild(final Construct scope, final String id, final IVpc vpc, final Map<String, String> environmentVariables, final String buildSpec) {
@@ -164,6 +167,13 @@ public CodeBuild(final Construct scope, final String id, final CodeBuildProps pr
164167

165168
this.customResource.getNode().addDependency(buildCompleteRule);
166169
this.customResource.getNode().addDependency(reportBuildFunction);
170+
171+
// Add external dependencies (e.g., NAT Gateway, ECR Registry)
172+
if (props.getDependencies() != null) {
173+
for (software.constructs.IDependable dep : props.getDependencies()) {
174+
this.customResource.getNode().addDependency(dep);
175+
}
176+
}
167177
}
168178

169179
public Project getCodeBuildProject() {

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import java.util.List;
77

88
public class Vpc extends Construct {
9-
private final IVpc vpc;
9+
private final software.amazon.awscdk.services.ec2.Vpc vpc;
1010

1111
public static class VpcProps {
1212
private String prefix = "workshop";
@@ -64,4 +64,11 @@ public Vpc(final Construct scope, final String id, final VpcProps props) {
6464
public IVpc getVpc() {
6565
return vpc;
6666
}
67+
68+
/**
69+
* Returns the concrete VPC (not interface) to access NAT gateways for dependencies.
70+
*/
71+
public software.amazon.awscdk.services.ec2.Vpc getConcreteVpc() {
72+
return vpc;
73+
}
6774
}

infra/cfn/java-spring-ai-agents-stack.yaml

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ Resources:
714714
Fn::GetAtt:
715715
- CodeBuildRoleE9A44575
716716
- Arn
717-
ContentHash: "1769285698737"
717+
ContentHash: "1769326880641"
718718
ProjectName:
719719
Ref: CodeBuildProjectA0FF5539
720720
ServiceToken:
@@ -801,12 +801,12 @@ Resources:
801801
Environment:
802802
ComputeType: BUILD_GENERAL1_MEDIUM
803803
EnvironmentVariables:
804-
- Name: GIT_BRANCH
805-
Type: PLAINTEXT
806-
Value: main
807804
- Name: TEMPLATE_TYPE
808805
Type: PLAINTEXT
809806
Value: java-spring-ai-agents
807+
- Name: GIT_BRANCH
808+
Type: PLAINTEXT
809+
Value: main
810810
Image: aws/codebuild/amazonlinux2-x86_64-standard:5.0
811811
ImagePullCredentialsType: CODEBUILD
812812
PrivilegedMode: false
@@ -2398,15 +2398,37 @@ Resources:
23982398
PlaceholderImageBuild084DA641:
23992399
DeletionPolicy: Delete
24002400
DependsOn:
2401+
- EcrRegistryTemplateD54113AB
24012402
- PlaceholderImageBuildCompleteRuleAllowEventRuleWorkshopStackPlaceholderImageBuildReportLambdaFunction17D5E0E6696B4706
24022403
- PlaceholderImageBuildCompleteRuleD3DA254B
24032404
- PlaceholderImageBuildReportLambdaFunctionD1A0B620
2405+
- VpcIGW488B0FEB
2406+
- VpcPrivateSubnet1DefaultRouteF704DE9F
2407+
- VpcPrivateSubnet1RouteTable901BAEEE
2408+
- VpcPrivateSubnet1RouteTableAssociation2BC202CB
2409+
- VpcPrivateSubnet1Subnet67A4DBCB
2410+
- VpcPrivateSubnet2DefaultRoute5FAC9901
2411+
- VpcPrivateSubnet2RouteTable1EA00C9D
2412+
- VpcPrivateSubnet2RouteTableAssociationFA51927B
2413+
- VpcPrivateSubnet2SubnetC8EB537D
2414+
- VpcPublicSubnet1DefaultRoute0F5C6C43
2415+
- VpcPublicSubnet1EIP2293BAA3
2416+
- VpcPublicSubnet1NATGateway8185E366
2417+
- VpcPublicSubnet1RouteTable431DD755
2418+
- VpcPublicSubnet1RouteTableAssociationBBCB7AA1
2419+
- VpcPublicSubnet1Subnet8E8DEDC0
2420+
- VpcPublicSubnet2DefaultRouteD629179A
2421+
- VpcPublicSubnet2RouteTable77FB35FC
2422+
- VpcPublicSubnet2RouteTableAssociation3AFE92E6
2423+
- VpcPublicSubnet2SubnetA811849C
2424+
- VpcC3027511
2425+
- VpcVPCGW42EC8516
24042426
Properties:
24052427
CodeBuildIamRoleArn:
24062428
Fn::GetAtt:
24072429
- PlaceholderImageBuildRole66BA72FE
24082430
- Arn
2409-
ContentHash: "1769285699069"
2431+
ContentHash: "1769326881001"
24102432
ProjectName:
24112433
Ref: PlaceholderImageBuildProjectC08F4D66
24122434
ServiceToken:
@@ -2514,8 +2536,23 @@ Resources:
25142536
build:
25152537
commands:
25162538
- |
2539+
set -e
25172540
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
2518-
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
2541+
ECR_REGISTRY=$ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
2542+
2543+
# ECR login with retry (network can be slow on first attempt)
2544+
for i in 1 2 3; do
2545+
echo "ECR login attempt $i..."
2546+
if aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REGISTRY; then
2547+
echo "ECR login successful"
2548+
break
2549+
fi
2550+
if [ $i -eq 3 ]; then
2551+
echo "ECR login failed after 3 attempts"
2552+
exit 1
2553+
fi
2554+
sleep 10
2555+
done
25192556
25202557
# Create placeholder Dockerfile
25212558
cat > /tmp/Dockerfile << 'EOF'
@@ -2528,8 +2565,8 @@ Resources:
25282565
25292566
# Build and push placeholder image (create-on-push creates repo)
25302567
docker build -t placeholder /tmp
2531-
docker tag placeholder $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/aiagent:latest
2532-
docker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/aiagent:latest
2568+
docker tag placeholder $ECR_REGISTRY/aiagent:latest
2569+
docker push $ECR_REGISTRY/aiagent:latest
25332570
Type: NO_SOURCE
25342571
TimeoutInMinutes: 30
25352572
VpcConfig:
@@ -3558,7 +3595,7 @@ Resources:
35583595
- Ref: AWS::AccountId
35593596
- "-"
35603597
- Ref: AWS::Region
3561-
- "-20260124211458"
3598+
- "-20260125084120"
35623599
PublicAccessBlockConfiguration:
35633600
BlockPublicAcls: true
35643601
BlockPublicPolicy: true

infra/scripts/templates/java-ai-agents.sh

100644100755
File mode changed.

0 commit comments

Comments
 (0)