Skip to content

Commit b02b153

Browse files
author
Yuriy Bezsonov
committed
update infrastructure
1 parent 4bf1f77 commit b02b153

13 files changed

Lines changed: 2750 additions & 81 deletions

File tree

infra/cdk/.jqwik-database

4 Bytes
Binary file not shown.

infra/cdk/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,5 +119,13 @@
119119
<version>${junit.version}</version>
120120
<scope>test</scope>
121121
</dependency>
122+
123+
<!-- Property-based testing with jqwik -->
124+
<dependency>
125+
<groupId>net.jqwik</groupId>
126+
<artifactId>jqwik</artifactId>
127+
<version>1.9.2</version>
128+
<scope>test</scope>
129+
</dependency>
122130
</dependencies>
123131
</project>

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

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,62 @@ public class Ide extends Construct {
4444
private final Role lambdaRole;
4545
private CustomResource passwordResource;
4646

47+
/**
48+
* Architecture enum for IDE instances.
49+
* Determines instance types and AMI selection.
50+
*/
51+
public enum IdeArch {
52+
ARM64("arm64", "aarch64"),
53+
X86_64("x86_64", "x86_64");
54+
55+
private final String awsValue;
56+
private final String unameValue;
57+
58+
IdeArch(String awsValue, String unameValue) {
59+
this.awsValue = awsValue;
60+
this.unameValue = unameValue;
61+
}
62+
63+
public String getAwsValue() { return awsValue; }
64+
public String getUnameValue() { return unameValue; }
65+
}
66+
67+
/**
68+
* IDE type enum for selecting between code-server and AWS Code Editor.
69+
*/
70+
public enum IdeType {
71+
CODE_EDITOR("code-editor"),
72+
VSCODE("vscode");
73+
74+
private final String scriptName;
75+
76+
IdeType(String scriptName) {
77+
this.scriptName = scriptName;
78+
}
79+
80+
public String getScriptName() { return scriptName; }
81+
}
82+
4783
public static class IdeProps {
4884
private String instanceName = "ide";
4985
private int diskSize = 50;
5086
private IVpc vpc;
51-
private IMachineImage machineImage = MachineImage.latestAmazonLinux2023();
52-
private List<String> instanceTypes = Arrays.asList("m7i-flex.xlarge", "m7a.xlarge", "m6i.xlarge", "m6a.xlarge", "m5.xlarge", "t3.xlarge");
87+
private IdeArch ideArch = IdeArch.ARM64;
88+
private IdeType ideType = IdeType.CODE_EDITOR;
5389
private List<ISecurityGroup> additionalSecurityGroups = new ArrayList<>();
5490
private int bootstrapTimeoutMinutes = 30;
5591
private String gitBranch = "main";
5692
private String templateType = "base";
5793
private Role ideRole;
5894

95+
// Architecture-specific instance type lists
96+
private static final List<String> ARM64_INSTANCE_TYPES =
97+
Arrays.asList("m7g.xlarge", "m6g.xlarge", "c7g.xlarge", "t4g.xlarge");
98+
private static final List<String> X86_64_INSTANCE_TYPES =
99+
Arrays.asList("m7i-flex.xlarge", "m7a.xlarge", "m6i.xlarge", "m6a.xlarge", "m5.xlarge", "t3.xlarge");
100+
// Old instance list (x86_64) for reference:
101+
// Arrays.asList("m7i-flex.xlarge", "m7a.xlarge", "m6i.xlarge", "m6a.xlarge", "m5.xlarge", "t3.xlarge");
102+
59103
public static IdeProps.Builder builder() { return new Builder(); }
60104

61105
public static class Builder {
@@ -64,8 +108,8 @@ public static class Builder {
64108
public Builder instanceName(String instanceName) { props.instanceName = instanceName; return this; }
65109
public Builder diskSize(int diskSize) { props.diskSize = diskSize; return this; }
66110
public Builder vpc(IVpc vpc) { props.vpc = vpc; return this; }
67-
public Builder machineImage(IMachineImage machineImage) { props.machineImage = machineImage; return this; }
68-
public Builder instanceTypes(List<String> instanceTypes) { props.instanceTypes = instanceTypes; return this; }
111+
public Builder ideArch(IdeArch ideArch) { props.ideArch = ideArch; return this; }
112+
public Builder ideType(IdeType ideType) { props.ideType = ideType; return this; }
69113
public Builder additionalSecurityGroups(List<ISecurityGroup> additionalSecurityGroups) { props.additionalSecurityGroups = additionalSecurityGroups; return this; }
70114
public Builder bootstrapTimeoutMinutes(int bootstrapTimeoutMinutes) { props.bootstrapTimeoutMinutes = bootstrapTimeoutMinutes; return this; }
71115
public Builder gitBranch(String gitBranch) { props.gitBranch = gitBranch; return this; }
@@ -78,8 +122,20 @@ public static class Builder {
78122
public String getInstanceName() { return instanceName; }
79123
public int getDiskSize() { return diskSize; }
80124
public IVpc getVpc() { return vpc; }
81-
public IMachineImage getMachineImage() { return machineImage; }
82-
public List<String> getInstanceTypes() { return instanceTypes; }
125+
public IdeArch getIdeArch() { return ideArch; }
126+
public IdeType getIdeType() { return ideType; }
127+
128+
public IMachineImage getMachineImage() {
129+
return ideArch == IdeArch.ARM64
130+
? MachineImage.latestAmazonLinux2023(AmazonLinux2023ImageSsmParameterProps.builder()
131+
.cpuType(AmazonLinuxCpuType.ARM_64).build())
132+
: MachineImage.latestAmazonLinux2023();
133+
}
134+
135+
public List<String> getInstanceTypes() {
136+
return ideArch == IdeArch.ARM64 ? ARM64_INSTANCE_TYPES : X86_64_INSTANCE_TYPES;
137+
}
138+
83139
public List<ISecurityGroup> getAdditionalSecurityGroups() { return additionalSecurityGroups; }
84140
public int getBootstrapTimeoutMinutes() { return bootstrapTimeoutMinutes; }
85141
public String getGitBranch() { return gitBranch; }
@@ -263,6 +319,8 @@ public Ide(final Construct scope, final String id, final IdeProps props) {
263319
.replace("${GIT_BRANCH}", gitBranch)
264320
.replace("${AWS_REGION}", Aws.REGION)
265321
.replace("${TEMPLATE_TYPE}", templateType)
322+
.replace("${ARCH}", props.getIdeArch().getUnameValue())
323+
.replace("${IDE_TYPE}", props.getIdeType().getScriptName())
266324
.replace("${WAIT_CONDITION_HANDLE_URL}", waitHandle.getRef());
267325

268326
userData.addCommands(userDataContent);
@@ -334,8 +392,19 @@ public Ide(final Construct scope, final String id, final IdeProps props) {
334392
.build();
335393
waitCondition.getNode().addDependency(ec2InstanceResource);
336394

337-
var ideUrlOutput = CfnOutput.Builder.create(this, "Url")
338-
.value("https://" + distribution.getDistributionDomainName())
395+
// Build URL based on IDE type
396+
String ideUrl;
397+
if (props.getIdeType() == IdeType.CODE_EDITOR) {
398+
// Token-based URL for Code Editor (seamless Workshop Studio access)
399+
ideUrl = "https://" + distribution.getDistributionDomainName() +
400+
"/?folder=/home/ec2-user/environment&tkn=" + getIdePassword(instanceName);
401+
} else {
402+
// Standard URL for code-server (password entered via login page)
403+
ideUrl = "https://" + distribution.getDistributionDomainName();
404+
}
405+
406+
CfnOutput.Builder.create(this, "Url")
407+
.value(ideUrl)
339408
.description("Workshop IDE Url")
340409
.exportName(instanceName + "-url")
341410
.build();

infra/cdk/src/main/resources/userdata.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ set -e
88
export GIT_BRANCH="${GIT_BRANCH}"
99
export AWS_REGION="${AWS_REGION}"
1010
export TEMPLATE_TYPE="${TEMPLATE_TYPE}"
11+
export ARCH="${ARCH}"
12+
export IDE_TYPE="${IDE_TYPE}"
1113
export WAIT_CONDITION_HANDLE_URL="${WAIT_CONDITION_HANDLE_URL}"
1214

1315
# Setup logging
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package sample.com.constructs;
2+
3+
import net.jqwik.api.*;
4+
import org.junit.jupiter.api.Test;
5+
import sample.com.constructs.Ide.IdeArch;
6+
import sample.com.constructs.Ide.IdeProps;
7+
8+
import java.util.List;
9+
10+
import static org.junit.jupiter.api.Assertions.*;
11+
12+
/**
13+
* Property-based tests for Ide construct properties.
14+
*
15+
* **Feature: arm64-code-editor-support, Property 1: Architecture determines instance types**
16+
* **Validates: Requirements 1.2, 1.3, 7.1, 7.2**
17+
*/
18+
public class IdePropsTest {
19+
20+
/**
21+
* Property 1: Architecture determines instance types
22+
* For any architecture value (ARM64 or X86_64), the returned instance type list
23+
* SHALL contain only instances of the matching architecture family.
24+
* - ARM64: instance types contain 'g' suffix (m7g, m6g, c7g, t4g)
25+
* - X86_64: instance types do NOT contain 'g' suffix before size (m7i, m6i, m5, t3)
26+
*/
27+
@Property(tries = 100)
28+
void architectureDeterminesInstanceTypes(@ForAll("ideArchProvider") IdeArch arch) {
29+
// Given
30+
IdeProps props = IdeProps.builder()
31+
.ideArch(arch)
32+
.build();
33+
34+
// When
35+
List<String> instanceTypes = props.getInstanceTypes();
36+
37+
// Then
38+
assertNotNull(instanceTypes);
39+
assertFalse(instanceTypes.isEmpty());
40+
41+
if (arch == IdeArch.ARM64) {
42+
// ARM64 instances have 'g' suffix (Graviton): m7g, m6g, c7g, t4g
43+
for (String instanceType : instanceTypes) {
44+
assertTrue(
45+
instanceType.matches(".*[0-9]g\\..*"),
46+
"ARM64 instance type should have 'g' suffix (Graviton): " + instanceType
47+
);
48+
}
49+
} else {
50+
// X86_64 instances do NOT have 'g' suffix before size
51+
for (String instanceType : instanceTypes) {
52+
assertFalse(
53+
instanceType.matches(".*[0-9]g\\..*"),
54+
"X86_64 instance type should NOT have 'g' suffix: " + instanceType
55+
);
56+
}
57+
}
58+
}
59+
60+
@Provide
61+
Arbitrary<IdeArch> ideArchProvider() {
62+
return Arbitraries.of(IdeArch.ARM64, IdeArch.X86_64);
63+
}
64+
65+
/**
66+
* Unit test: ARM64 returns Graviton instance types
67+
*/
68+
@Test
69+
void arm64ReturnsGravitonInstanceTypes() {
70+
IdeProps props = IdeProps.builder()
71+
.ideArch(IdeArch.ARM64)
72+
.build();
73+
74+
List<String> instanceTypes = props.getInstanceTypes();
75+
76+
assertEquals(4, instanceTypes.size());
77+
assertTrue(instanceTypes.contains("m7g.xlarge"));
78+
assertTrue(instanceTypes.contains("m6g.xlarge"));
79+
assertTrue(instanceTypes.contains("c7g.xlarge"));
80+
assertTrue(instanceTypes.contains("t4g.xlarge"));
81+
}
82+
83+
/**
84+
* Unit test: X86_64 returns Intel/AMD instance types
85+
*/
86+
@Test
87+
void x86_64ReturnsIntelAmdInstanceTypes() {
88+
IdeProps props = IdeProps.builder()
89+
.ideArch(IdeArch.X86_64)
90+
.build();
91+
92+
List<String> instanceTypes = props.getInstanceTypes();
93+
94+
assertEquals(6, instanceTypes.size());
95+
assertTrue(instanceTypes.contains("m7i-flex.xlarge"));
96+
assertTrue(instanceTypes.contains("m7a.xlarge"));
97+
assertTrue(instanceTypes.contains("m6i.xlarge"));
98+
assertTrue(instanceTypes.contains("m6a.xlarge"));
99+
assertTrue(instanceTypes.contains("m5.xlarge"));
100+
assertTrue(instanceTypes.contains("t3.xlarge"));
101+
}
102+
103+
/**
104+
* Unit test: Default architecture is ARM64
105+
*/
106+
@Test
107+
void defaultArchitectureIsArm64() {
108+
IdeProps props = IdeProps.builder().build();
109+
110+
assertEquals(IdeArch.ARM64, props.getIdeArch());
111+
// Should return ARM64 instance types by default
112+
assertTrue(props.getInstanceTypes().contains("m7g.xlarge"));
113+
}
114+
}

infra/cfn/base-stack.yaml

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -713,30 +713,19 @@ Resources:
713713
Fn::GetAtt:
714714
- IdeInstanceLauncherFunction803C5A2A
715715
- Arn
716-
InstanceName: ide
717-
IamInstanceProfileArn:
718-
Fn::GetAtt:
719-
- IdeInstanceProfile61B92038
720-
- Arn
721-
VolumeSize: "50"
722716
SubnetIds:
723717
Fn::Join:
724718
- ""
725719
- - Ref: VpcPublicSubnet1Subnet8E8DEDC0
726720
- ","
727721
- Ref: VpcPublicSubnet2SubnetA811849C
728-
SecurityGroupIds:
729-
Fn::Join:
730-
- ""
731-
- - Fn::GetAtt:
732-
- IdeSecurityGroup73B02454
733-
- GroupId
734-
- ","
735-
- Fn::GetAtt:
736-
- IdeInternalSecurityGroupB0A5D76B
737-
- GroupId
738-
ImageId:
739-
Ref: SsmParameterValueawsserviceamiamazonlinuxlatestal2023amikernel61x8664C96584B6F00A464EAD1953AFF4B05118Parameter
722+
VolumeSize: "50"
723+
IamInstanceProfileArn:
724+
Fn::GetAtt:
725+
- IdeInstanceProfile61B92038
726+
- Arn
727+
InstanceName: ide
728+
InstanceTypes: m7g.xlarge,m6g.xlarge,c7g.xlarge,t4g.xlarge
740729
UserData:
741730
Fn::Base64:
742731
Fn::Join:
@@ -756,6 +745,8 @@ Resources:
756745
- |-
757746
"
758747
export TEMPLATE_TYPE="base"
748+
export ARCH="aarch64"
749+
export IDE_TYPE="code-editor"
759750
export WAIT_CONDITION_HANDLE_URL="
760751
- Ref: IdeWaitConditionHandleE8345861
761752
- |-
@@ -870,7 +861,18 @@ Resources:
870861
"
871862
exit 1
872863
fi
873-
InstanceTypes: m7i-flex.xlarge,m7a.xlarge,m6i.xlarge,m6a.xlarge,m5.xlarge,t3.xlarge
864+
ImageId:
865+
Ref: SsmParameterValueawsserviceamiamazonlinuxlatestal2023amikernel61arm64C96584B6F00A464EAD1953AFF4B05118Parameter
866+
SecurityGroupIds:
867+
Fn::Join:
868+
- ""
869+
- - Fn::GetAtt:
870+
- IdeSecurityGroup73B02454
871+
- GroupId
872+
- ","
873+
- Fn::GetAtt:
874+
- IdeInternalSecurityGroupB0A5D76B
875+
- GroupId
874876
UpdateReplacePolicy: Delete
875877
DeletionPolicy: Delete
876878
IdeEipAssociationDFF81215:
@@ -1499,11 +1501,11 @@ Resources:
14991501
- Arn
15001502
ProjectName:
15011503
Ref: CodeBuildProjectA0FF5539
1504+
ContentHash: "1765978900817"
15021505
CodeBuildIamRoleArn:
15031506
Fn::GetAtt:
15041507
- CodeBuildRoleE9A44575
15051508
- Arn
1506-
ContentHash: "1765969925813"
15071509
DependsOn:
15081510
- CodeBuildCompleteRuleAllowEventRuleWorkshopStackCodeBuildReportLambdaFunctionD77C60919E0B0C89
15091511
- CodeBuildCompleteRuleEE9277E8
@@ -1520,6 +1522,10 @@ Outputs:
15201522
- Fn::GetAtt:
15211523
- IdeDistribution042A6660
15221524
- DomainName
1525+
- /?folder=/home/ec2-user/environment&tkn=
1526+
- Fn::GetAtt:
1527+
- IdePasswordResource07883F17
1528+
- password
15231529
Export:
15241530
Name: ide-url
15251531
IdePassword51C06AAD:
@@ -1531,7 +1537,7 @@ Outputs:
15311537
Export:
15321538
Name: ide-password
15331539
Parameters:
1534-
SsmParameterValueawsserviceamiamazonlinuxlatestal2023amikernel61x8664C96584B6F00A464EAD1953AFF4B05118Parameter:
1540+
SsmParameterValueawsserviceamiamazonlinuxlatestal2023amikernel61arm64C96584B6F00A464EAD1953AFF4B05118Parameter:
15351541
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
1536-
Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64
1542+
Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-arm64
15371543

0 commit comments

Comments
 (0)