Skip to content

Commit

Permalink
patch(feat): octo-aws-cdk | simple-aws-subnet with ability to associa…
Browse files Browse the repository at this point in the history
…te with siblings.
  • Loading branch information
rash805115 committed Dec 24, 2024
1 parent 04a3cbe commit 75698a9
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ import {
type App,
type Container,
type Region,
SubnetType,
TestContainer,
TestModuleContainer,
TestStateProvider,
stub,
} from '@quadnix/octo';
import { AddNetworkAclResourceAction } from '../../../resources/network-acl/actions/add-network-acl.resource.action.js';
import type { NetworkAcl } from '../../../resources/network-acl/index.js';
import { AddRouteTableResourceAction } from '../../../resources/route-table/actions/add-route-table.resource.action.js';
import { AddSubnetResourceAction } from '../../../resources/subnet/actions/add-subnet.resource.action.js';
import { AwsSubnetModule } from './aws-subnet.module.js';
Expand Down Expand Up @@ -87,11 +90,11 @@ describe('AwsSubnetModule UT', () => {
inputs: {
awsRegionAZ: 'us-east-1a',
awsRegionId: 'us-east-1',
internetGatewayResource: '${{testModule.resource.igw-aws-us-east-1a}}',
region: '${{testModule.model.region}}',
internetGatewayResource: stub('${{testModule.resource.igw-aws-us-east-1a}}'),
region: stub('${{testModule.model.region}}'),
subnetCidrBlock: '10.0.0.0/24',
subnetName: 'test-private-subnet',
vpcResource: '${{testModule.resource.vpc-aws-us-east-1a}}',
vpcResource: stub('${{testModule.resource.vpc-aws-us-east-1a}}'),
},
moduleId: 'subnet',
type: AwsSubnetModule,
Expand Down Expand Up @@ -187,11 +190,11 @@ describe('AwsSubnetModule UT', () => {
inputs: {
awsRegionAZ: 'us-east-1a',
awsRegionId: 'us-east-1',
internetGatewayResource: '${{testModule.resource.igw-aws-us-east-1a}}',
region: '${{testModule.model.region}}',
internetGatewayResource: stub('${{testModule.resource.igw-aws-us-east-1a}}'),
region: stub('${{testModule.model.region}}'),
subnetCidrBlock: '10.0.0.0/24',
subnetName: 'test-private-subnet',
vpcResource: '${{testModule.resource.vpc-aws-us-east-1a}}',
vpcResource: stub('${{testModule.resource.vpc-aws-us-east-1a}}'),
},
moduleId: 'subnet',
type: AwsSubnetModule,
Expand Down Expand Up @@ -223,27 +226,34 @@ describe('AwsSubnetModule UT', () => {
[],
]
`);
expect((result1.resourceDiffs[0][2].diff.node as NetworkAcl).properties).toMatchInlineSnapshot(`
{
"awsRegionId": "us-east-1",
"entries": [],
}
`);

const { app: app2 } = await setup(testModuleContainer);
await testModuleContainer.runModule<AwsSubnetModule>({
inputs: {
awsRegionAZ: 'us-east-1a',
awsRegionId: 'us-east-1',
internetGatewayResource: '${{testModule.resource.igw-aws-us-east-1a}}',
region: '${{testModule.model.region}}',
internetGatewayResource: stub('${{testModule.resource.igw-aws-us-east-1a}}'),
region: stub('${{testModule.model.region}}'),
subnetCidrBlock: '10.0.0.0/24',
subnetName: 'test-private-subnet',
subnetOptions: JSON.stringify({
subnetOptions: {
disableSubnetIntraNetwork: true,
}),
vpcResource: '${{testModule.resource.vpc-aws-us-east-1a}}',
subnetType: SubnetType.PRIVATE,
},
vpcResource: stub('${{testModule.resource.vpc-aws-us-east-1a}}'),
},
moduleId: 'subnet',
type: AwsSubnetModule,
});

const result3 = await testModuleContainer.commit(app2, { enableResourceCapture: true });
expect(result3.resourceDiffs).toMatchInlineSnapshot(`
const result2 = await testModuleContainer.commit(app2, { enableResourceCapture: true });
expect(result2.resourceDiffs).toMatchInlineSnapshot(`
[
[
{
Expand Down Expand Up @@ -284,8 +294,8 @@ describe('AwsSubnetModule UT', () => {
`);

const { app: app3 } = await setup(testModuleContainer);
const result2 = await testModuleContainer.commit(app3, { enableResourceCapture: true });
expect(result2.resourceDiffs).toMatchInlineSnapshot(`
const result3 = await testModuleContainer.commit(app3, { enableResourceCapture: true });
expect(result3.resourceDiffs).toMatchInlineSnapshot(`
[
[
{
Expand All @@ -311,4 +321,140 @@ describe('AwsSubnetModule UT', () => {
]
`);
});

it('should associate subnet with siblings', async () => {
const { app: app1 } = await setup(testModuleContainer);
await testModuleContainer.runModule<AwsSubnetModule>({
inputs: {
awsRegionAZ: 'us-east-1a',
awsRegionId: 'us-east-1',
internetGatewayResource: stub('${{testModule.resource.igw-aws-us-east-1a}}'),
region: stub('${{testModule.model.region}}'),
subnetCidrBlock: '10.0.0.0/24',
subnetName: 'test-private-subnet',
vpcResource: stub('${{testModule.resource.vpc-aws-us-east-1a}}'),
},
moduleId: 'subnet1',
type: AwsSubnetModule,
});
await testModuleContainer.runModule<AwsSubnetModule>({
inputs: {
awsRegionAZ: 'us-east-1a',
awsRegionId: 'us-east-1',
internetGatewayResource: stub('${{testModule.resource.igw-aws-us-east-1a}}'),
region: stub('${{testModule.model.region}}'),
subnetCidrBlock: '10.0.1.0/24',
subnetName: 'test-public-subnet',
subnetOptions: {
disableSubnetIntraNetwork: false,
subnetType: SubnetType.PUBLIC,
},
subnetSiblings: [
{
subnetCidrBlock: stub('${{subnet1.input.subnetCidrBlock}}'),
subnetName: stub('${{subnet1.input.subnetName}}'),
},
],
vpcResource: stub('${{testModule.resource.vpc-aws-us-east-1a}}'),
},
moduleId: 'subnet2',
type: AwsSubnetModule,
});

const result1 = await testModuleContainer.commit(app1, { enableResourceCapture: true });
expect(result1.resourceDiffs).toMatchInlineSnapshot(`
[
[
{
"action": "add",
"field": "resourceId",
"node": "@octo/subnet=subnet-region-test-private-subnet",
"value": "@octo/subnet=subnet-region-test-private-subnet",
},
{
"action": "add",
"field": "resourceId",
"node": "@octo/route-table=rt-region-test-private-subnet",
"value": "@octo/route-table=rt-region-test-private-subnet",
},
{
"action": "add",
"field": "resourceId",
"node": "@octo/network-acl=nacl-region-test-private-subnet",
"value": "@octo/network-acl=nacl-region-test-private-subnet",
},
{
"action": "add",
"field": "resourceId",
"node": "@octo/subnet=subnet-region-test-public-subnet",
"value": "@octo/subnet=subnet-region-test-public-subnet",
},
{
"action": "add",
"field": "resourceId",
"node": "@octo/route-table=rt-region-test-public-subnet",
"value": "@octo/route-table=rt-region-test-public-subnet",
},
{
"action": "add",
"field": "resourceId",
"node": "@octo/network-acl=nacl-region-test-public-subnet",
"value": "@octo/network-acl=nacl-region-test-public-subnet",
},
],
[],
]
`);
expect((result1.resourceDiffs[0][5].diff.node as NetworkAcl).properties).toMatchInlineSnapshot(`
{
"awsRegionId": "us-east-1",
"entries": [
{
"CidrBlock": "0.0.0.0/0",
"Egress": false,
"PortRange": {
"From": -1,
"To": -1,
},
"Protocol": "-1",
"RuleAction": "allow",
"RuleNumber": 1,
},
{
"CidrBlock": "10.0.1.0/24",
"Egress": true,
"PortRange": {
"From": -1,
"To": -1,
},
"Protocol": "-1",
"RuleAction": "allow",
"RuleNumber": 1,
},
{
"CidrBlock": "10.0.0.0/24",
"Egress": false,
"PortRange": {
"From": -1,
"To": -1,
},
"Protocol": "-1",
"RuleAction": "allow",
"RuleNumber": 11,
},
{
"CidrBlock": "10.0.0.0/24",
"Egress": true,
"PortRange": {
"From": -1,
"To": -1,
},
"Protocol": "-1",
"RuleAction": "allow",
"RuleNumber": 11,
},
],
}
`);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,12 @@ export class AwsSubnetModuleSchema {

subnetName = Schema<string>();

@Validate({ options: {} }, (value: any): object => {
if (typeof value === 'string') {
return JSON.parse(value);
} else {
return value;
}
})
subnetOptions? = Schema<{ disableSubnetIntraNetwork: boolean; subnetType: SubnetType }>({
disableSubnetIntraNetwork: false,
subnetType: SubnetType.PRIVATE,
});

subnetSiblings? = Schema<string[]>([]);
subnetSiblings? = Schema<{ subnetCidrBlock: string; subnetName: string }[]>([]);

@Validate({ options: { isResource: { NODE_NAME: 'vpc' } } })
vpcResource = Schema<AResource<VpcResourceSchema, any>>();
Expand All @@ -73,11 +66,11 @@ export class AwsSubnetModule extends AModule<AwsSubnetModuleSchema, AwsSubnet> {
region.addSubnet(subnet);

// Associate subnet with siblings.
const siblingSubnets = (region.getChildren('subnet')['subnet'] || []).map((d) => d.to as Subnet);
for (const siblingSubnetName of inputs.subnetSiblings || []) {
const siblingSubnet = siblingSubnets.find((s) => s.subnetName === siblingSubnetName);
const regionSubnets = (region.getChildren('subnet')['subnet'] || []).map((d) => d.to as Subnet);
for (const { subnetName } of inputs.subnetSiblings || []) {
const siblingSubnet = regionSubnets.find((s) => s.subnetName === subnetName);
if (!siblingSubnet) {
throw new Error(`Sibling subnet "${siblingSubnetName}" not found!`);
throw new Error(`Sibling subnet "${subnetName}" not found!`);
}
subnet.updateNetworkingRules(siblingSubnet, true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export class AddSubnetModelAction implements IModelAction<AwsSubnetModule> {
const subnet = diff.node as AwsSubnet;

const subnetCidrBlock = actionInputs.inputs.subnetCidrBlock;
const subnetSiblings = (subnet.getSiblings('subnet')['subnet'] || []).map((d) => d.to as Subnet);
const vpc = actionInputs.inputs.vpcResource;
const internetGateway = actionInputs.inputs.internetGatewayResource;

Expand Down Expand Up @@ -121,26 +120,6 @@ export class AddSubnetModelAction implements IModelAction<AwsSubnetModule> {
}
}
}
// Create Network ACL entries - inter network.
for (const subnetSibling of subnetSiblings) {
const subnetNAclLastEntryRuleNumber = Math.max(...subnetNAclEntries.map((e) => e.RuleNumber), 0);
subnetNAclEntries.push({
CidrBlock: subnetSibling.properties.CidrBlock,
Egress: false,
PortRange: { From: -1, To: -1 },
Protocol: '-1', // All.
RuleAction: 'allow',
RuleNumber: Math.ceil(subnetNAclLastEntryRuleNumber / 10) * 10 + 1,
});
subnetNAclEntries.push({
CidrBlock: subnetSibling.properties.CidrBlock,
Egress: true,
PortRange: { From: -1, To: -1 },
Protocol: '-1', // All.
RuleAction: 'allow',
RuleNumber: Math.ceil(subnetNAclLastEntryRuleNumber / 10) * 10 + 1,
});
}

// Create Network ACL.
const subnetNAcl = new NetworkAcl(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import {
Action,
type ActionOutputs,
type Diff,
DiffAction,
type EnhancedModuleSchema,
Factory,
type IModelAction,
} from '@quadnix/octo';
import { NetworkAcl, type NetworkAclSchema } from '../../../../../../resources/network-acl/index.js';
import type { AwsSubnetModule } from '../../../aws-subnet.module.js';
import { AwsSubnet } from '../aws.subnet.model.js';

@Action(AwsSubnet)
export class UpdateSubnetAssociationModelAction implements IModelAction<AwsSubnetModule> {
filter(diff: Diff): boolean {
return (
diff.action === DiffAction.ADD &&
diff.node instanceof AwsSubnet &&
(diff.node.constructor as typeof AwsSubnet).NODE_NAME === 'subnet' &&
diff.field === 'sibling'
);
}

async handle(
diff: Diff,
actionInputs: EnhancedModuleSchema<AwsSubnetModule>,
actionOutputs: ActionOutputs,
): Promise<ActionOutputs> {
const subnet = diff.node as AwsSubnet;
const siblingSubnet = diff.value as AwsSubnet;

const siblingSubnetInputs = actionInputs.inputs.subnetSiblings || [];
const siblingSubnetInput = siblingSubnetInputs.find((s) => s.subnetName === siblingSubnet.subnetName)!;
const subnetNAcl = actionInputs.resources[`nacl-${subnet.subnetId}`] as NetworkAcl;

const subnetNAclLastEntryRuleNumber = Math.max(...subnetNAcl.properties.entries.map((e) => e.RuleNumber), 0);

// Create Network ACL entries.
const subnetNAclEntries: NetworkAclSchema['properties']['entries'] = [];
subnetNAclEntries.push({
CidrBlock: siblingSubnetInput?.subnetCidrBlock,
Egress: false,
PortRange: { From: -1, To: -1 },
Protocol: '-1', // All.
RuleAction: 'allow',
RuleNumber: Math.ceil(subnetNAclLastEntryRuleNumber / 10) * 10 + 1,
});
subnetNAclEntries.push({
CidrBlock: siblingSubnetInput?.subnetCidrBlock,
Egress: true,
PortRange: { From: -1, To: -1 },
Protocol: '-1', // All.
RuleAction: 'allow',
RuleNumber: Math.ceil(subnetNAclLastEntryRuleNumber / 10) * 10 + 1,
});
subnetNAcl.properties.entries.push(...subnetNAclEntries);

actionOutputs[subnetNAcl.resourceId] = subnetNAcl;
return actionOutputs;
}
}

@Factory<UpdateSubnetAssociationModelAction>(UpdateSubnetAssociationModelAction)
export class UpdateSubnetAssociationModelActionFactory {
private static instance: UpdateSubnetAssociationModelAction;

static async create(): Promise<UpdateSubnetAssociationModelAction> {
if (!this.instance) {
this.instance = new UpdateSubnetAssociationModelAction();
}
return this.instance;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import './actions/add-subnet.model.action.js';
import './actions/update-subnet-association.model.action.js';

export { AwsSubnet } from './aws.subnet.model.js';
export { AwsSubnetSchema } from './aws.subnet.schema.js';

0 comments on commit 75698a9

Please sign in to comment.