Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix IPV6 cidr blocks #197

Merged
merged 4 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions integration/ec2/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: pulumi-aws-ec2
runtime: nodejs
description: ec2 integration test
127 changes: 127 additions & 0 deletions integration/ec2/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import * as aws from '@pulumi/aws';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import * as pulumicdk from '@pulumi/cdk';
import { SecretValue } from 'aws-cdk-lib/core';

class Ec2Stack extends pulumicdk.Stack {
constructor(app: pulumicdk.App, id: string, options?: pulumicdk.StackOptions) {
super(app, id, options);
const vpc = new ec2.Vpc(this, 'Vpc', {
maxAzs: 2,
ipProtocol: ec2.IpProtocol.DUAL_STACK,
vpnGateway: true,
ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'),
natGateways: 1,
vpnConnections: {
dynamic: {
ip: '1.2.3.4',
tunnelOptions: [
{
preSharedKeySecret: SecretValue.unsafePlainText('secretkey1234'),
},
{
preSharedKeySecret: SecretValue.unsafePlainText('secretkey5678'),
},
],
},
static: {
ip: '4.5.6.7',
staticRoutes: ['192.168.10.0/24', '192.168.20.0/24'],
},
},
subnetConfiguration: [
{
name: 'Public',
subnetType: ec2.SubnetType.PUBLIC,
},
{
name: 'Private',
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
},
{
name: 'Isolated',
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
],
restrictDefaultSecurityGroup: false,
});

vpc.addFlowLog('FlowLogs', {
destination: ec2.FlowLogDestination.toCloudWatchLogs(),
});

vpc.addGatewayEndpoint('Dynamo', {
service: ec2.GatewayVpcEndpointAwsService.DYNAMODB,
});
vpc.addInterfaceEndpoint('ecr', {
service: ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER,
});

new ec2.PrefixList(this, 'PrefixList', {});
const nacl = new ec2.NetworkAcl(this, 'NetworkAcl', {
vpc,
subnetSelection: { subnetType: ec2.SubnetType.PUBLIC },
});
nacl.addEntry('AllowAll', {
cidr: ec2.AclCidr.anyIpv4(),
ruleAction: ec2.Action.ALLOW,
ruleNumber: 100,
traffic: ec2.AclTraffic.allTraffic(),
});
new ec2.KeyPair(this, 'KeyPair');

const nlb = new elbv2.NetworkLoadBalancer(this, 'NLB1', { vpc });
new ec2.VpcEndpointService(this, 'EndpointService', {
vpcEndpointServiceLoadBalancers: [nlb],
allowedPrincipals: [new iam.ArnPrincipal('ec2.amazonaws.com')],
});
}
}

new pulumicdk.App(
'app',
(scope: pulumicdk.App) => {
new Ec2Stack(scope, 'teststack');
},
{
appOptions: {
remapCloudControlResource: (logicalId, typeName, props, options) => {
if (typeName === 'AWS::EC2::VPNGatewayRoutePropagation') {
const tableIds: string[] = props.RouteTableIds;
return tableIds.flatMap((tableId, i) => {
const id = i === 0 ? logicalId : `${logicalId}-${i}`;
return {
logicalId: id,
resource: new aws.ec2.VpnGatewayRoutePropagation(
id,
{
routeTableId: tableId,
vpnGatewayId: props.VpnGatewayId,
},
options,
),
};
});
}
if (typeName === 'AWS::EC2::NetworkAclEntry') {
return new aws.ec2.NetworkAclRule(logicalId, {
egress: props.Egress,
toPort: props.PortRange?.To,
fromPort: props.PortRange?.From,
protocol: props.Protocol,
ruleNumber: props.RuleNumber,
networkAclId: props.NetworkAclId,
ruleAction: props.RuleAction,
cidrBlock: props.CidrBlock,
ipv6CidrBlock: props.Ipv6CidrBlock,
icmpCode: props.Icmp?.Code,
icmpType: props.Icmp?.Type,
});
}
return undefined;
},
},
},
);
15 changes: 15 additions & 0 deletions integration/ec2/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "pulumi-aws-cdk",
"devDependencies": {
"@types/node": "^10.0.0"
},
"dependencies": {
"@pulumi/aws": "^6.0.0",
"@pulumi/aws-native": "^1.6.0",
"@pulumi/cdk": "^0.5.0",
"@pulumi/pulumi": "^3.0.0",
"aws-cdk-lib": "2.149.0",
"constructs": "10.3.0",
"esbuild": "^0.24.0"
}
}
18 changes: 18 additions & 0 deletions integration/ec2/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"strict": true,
"outDir": "bin",
"target": "es2019",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true
},
"include": [
"./*.ts"
]
}
9 changes: 9 additions & 0 deletions integration/examples_nodejs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ func TestApiGatewayDomain(t *testing.T) {
integration.ProgramTest(t, &test)
}

func TestEc2(t *testing.T) {
test := getJSBaseOptions(t).
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "ec2"),
})

integration.ProgramTest(t, &test)
}

func getJSBaseOptions(t *testing.T) integration.ProgramTestOptions {
base := getBaseOptions(t)
baseJS := base.With(integration.ProgramTestOptions{
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"devDependencies": {
"@aws-cdk/aws-apprunner-alpha": "2.20.0-alpha.0",
"@pulumi/aws": "^6.32.0",
"@pulumi/aws-native": "^1.0.0",
"@pulumi/aws-native": "^1.6.0",
"@pulumi/docker": "^4.5.0",
"@pulumi/pulumi": "3.121.0",
"@types/archiver": "^6.0.2",
Expand Down
38 changes: 27 additions & 11 deletions src/converters/app-converter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as cdk from 'aws-cdk-lib/core';
import * as pulumi from '@pulumi/pulumi';
import { AssemblyManifestReader, StackManifest } from '../assembly';
import { ConstructInfo, GraphBuilder, GraphNode } from '../graph';
import { ConstructInfo, Graph, GraphBuilder, GraphNode } from '../graph';
import { ArtifactConverter } from './artifact-converter';
import { lift, Mapping, AppComponent } from '../types';
import { CdkConstruct, ResourceAttributeMapping, ResourceMapping } from '../interop';
Expand Down Expand Up @@ -94,6 +94,7 @@
private readonly cdkStack: cdk.Stack;

private _stackResource?: CdkConstruct;
private readonly graph: Graph;

public get stackResource(): CdkConstruct {
if (!this._stackResource) {
Expand All @@ -105,17 +106,16 @@
constructor(host: AppComponent, readonly stack: StackManifest) {
super(host);
this.cdkStack = host.stacks[stack.id];
this.graph = GraphBuilder.build(this.stack);
}

public convert(dependencies: Set<ArtifactConverter>) {
const dependencyGraphNodes = GraphBuilder.build(this.stack);

// process parameters first because resources will reference them
for (const [logicalId, value] of Object.entries(this.stack.parameters ?? {})) {
this.mapParameter(logicalId, value.Type, value.Default);
}

for (const n of dependencyGraphNodes) {
for (const n of this.graph.nodes) {
if (n.construct.id === this.stack.id) {
this._stackResource = new CdkConstruct(`${this.app.name}/${n.construct.path}`, n.construct.id, {
parent: this.app.component,
Expand Down Expand Up @@ -156,8 +156,8 @@
}
}

for (let i = dependencyGraphNodes.length - 1; i >= 0; i--) {
const n = dependencyGraphNodes[i];
for (let i = this.graph.nodes.length - 1; i >= 0; i--) {
const n = this.graph.nodes[i];
if (!n.resource) {
(<CdkConstruct>this.constructs.get(n.construct)!).done();
}
Expand Down Expand Up @@ -344,7 +344,7 @@
}

return Object.entries(obj)
.filter(([_, v]) => !this.isNoValue(v))

Check warning on line 347 in src/converters/app-converter.ts

View workflow job for this annotation

GitHub Actions / Run lint

'_' is defined but never used
.reduce((result, [k, v]) => ({ ...result, [k]: this.processIntrinsics(v) }), {});
}

Expand All @@ -359,7 +359,23 @@
private resolveIntrinsic(fn: string, params: any) {
switch (fn) {
case 'Fn::GetAtt': {
debug(`Fn::GetAtt(${params[0]}, ${params[1]})`);
const logicalId = params[0];
const attributeName = params[1];
debug(`Fn::GetAtt(${logicalId}, ${attributeName})`);
// Special case for VPC Ipv6CidrBlocks
// Ipv6 cidr blocks are added to the VPC through a separate VpcCidrBlock resource
// Due to [pulumi/pulumi-aws-native#1798] the `Ipv6CidrBlocks` attribute will always be empty
// and we need to instead pull the `Ipv6CidrBlock` attribute from the VpcCidrBlock resource.
if (
logicalId in this.graph.vpcNodes &&
attributeName === 'Ipv6CidrBlocks' &&
this.graph.vpcNodes[logicalId].vpcCidrBlockNode?.logicalId
) {
return [
this.resolveAtt(this.graph.vpcNodes[logicalId].vpcCidrBlockNode.logicalId, 'Ipv6CidrBlock'),
];
}

return this.resolveAtt(params[0], params[1]);
}

Expand All @@ -375,23 +391,23 @@
case 'Fn::Base64':
return lift((str) => Buffer.from(str).toString('base64'), this.processIntrinsics(params));

case 'Fn::Cidr':
case 'Fn::Cidr': {
return lift(
([ipBlock, count, cidrBits]) =>
cidr({
ipBlock,
count,
cidrBits,
count: parseInt(count, 10),
cidrBits: parseInt(cidrBits, 10),
}).then((r) => r.subnets),
this.processIntrinsics(params),
);

}
case 'Fn::GetAZs':
return lift(([region]) => getAzs({ region }).then((r) => r.azs), this.processIntrinsics(params));

case 'Fn::Sub':
return lift((params) => {
const [template, vars] =

Check warning on line 410 in src/converters/app-converter.ts

View workflow job for this annotation

GitHub Actions / Run lint

'vars' is assigned a value but never used
typeof params === 'string' ? [params, undefined] : [params[0] as string, params[1]];

const parts: string[] = [];
Expand Down
Loading
Loading