-
Notifications
You must be signed in to change notification settings - Fork 4.3k
feat(cloudfront-origins): add ipAddressType property to Lambda Function URL origins #35458
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
base: main
Are you sure you want to change the base?
feat(cloudfront-origins): add ipAddressType property to Lambda Function URL origins #35458
Conversation
…on URL origins with dualstack default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(This review is outdated)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your contribution! I've added some advices.
* | ||
* @default 'dualstack' | ||
*/ | ||
readonly ipAddressType?: 'ipv4' | 'ipv6' | 'dualstack'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We cannot use union type in CDK. Could you please define new enum type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the quick review, and sorry for the elementary mistake.
i've added new enum.
@@ -76,6 +85,7 @@ export class FunctionUrlOrigin extends cloudfront.OriginBase { | |||
originProtocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY, | |||
originReadTimeout: this.props.readTimeout?.toSeconds(), | |||
originKeepaliveTimeout: this.props.keepaliveTimeout?.toSeconds(), | |||
ipAddressType: this.props.ipAddressType ?? 'dualstack', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the cloudformation default value of ipAddressType? (If we deploy cfn with specifying ipAddressType to undefined, is the IP address type set to dual stack?)
If that default value is not 'dualstack', this modification is a breaking change and you need to implement a feature flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the cloudformation default value of ipAddressType? (If we deploy cfn with specifying ipAddressType to undefined, is the IP address type set to dual stack?)
I checked in my personal account and if we don't explicitly define IpAddressType in CloudFormation, IpAddressType defaults to IPv4.
If that default value is not 'dualstack', this modification is a breaking change and you need to implement a feature flag.
I have removed the setting that makes dualstack the default and implemented only the ipAddressType property. Although I initially set dualstack as the default based on the description in #35450,
this would be a breaking change, so it might be better to limit this PR just adding the property for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your confirmation and I agree with your opinion.
Could you please add integ test? |
sorry for being late, i just added integ-test's snapshot. |
✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.
test('Does not include ipAddressType when not specified (uses CloudFormation default)', () => { | ||
const fn = new lambda.Function(stack, 'MyFunction', { | ||
code: lambda.Code.fromInline('exports.handler = async () => {};'), | ||
handler: 'index.handler', | ||
runtime: lambda.Runtime.NODEJS_20_X, | ||
}); | ||
|
||
const fnUrl = fn.addFunctionUrl({ | ||
authType: lambda.FunctionUrlAuthType.NONE, | ||
}); | ||
|
||
const origin = new FunctionUrlOrigin(fnUrl); | ||
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' }); | ||
|
||
expect(stack.resolve(originBindConfig.originProperty)).toEqual({ | ||
id: 'StackOriginLambdaFunctionURL', | ||
domainName: { | ||
'Fn::Select': [2, { 'Fn::Split': ['/', { 'Fn::GetAtt': ['MyFunctionFunctionUrlFF6DE78C', 'FunctionUrl'] }] }], | ||
}, | ||
customOriginConfig: { | ||
originProtocolPolicy: 'https-only', | ||
originSslProtocols: ['TLSv1.2'], | ||
}, | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this test is not necessary.
test('Does not include ipAddressType when not specified (uses CloudFormation default)', () => { | |
const fn = new lambda.Function(stack, 'MyFunction', { | |
code: lambda.Code.fromInline('exports.handler = async () => {};'), | |
handler: 'index.handler', | |
runtime: lambda.Runtime.NODEJS_20_X, | |
}); | |
const fnUrl = fn.addFunctionUrl({ | |
authType: lambda.FunctionUrlAuthType.NONE, | |
}); | |
const origin = new FunctionUrlOrigin(fnUrl); | |
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' }); | |
expect(stack.resolve(originBindConfig.originProperty)).toEqual({ | |
id: 'StackOriginLambdaFunctionURL', | |
domainName: { | |
'Fn::Select': [2, { 'Fn::Split': ['/', { 'Fn::GetAtt': ['MyFunctionFunctionUrlFF6DE78C', 'FunctionUrl'] }] }], | |
}, | |
customOriginConfig: { | |
originProtocolPolicy: 'https-only', | |
originSslProtocols: ['TLSv1.2'], | |
}, | |
}); | |
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
understood,i deleted this test case.
test('Correctly sets ipAddressType to dualstack', () => { | ||
const fn = new lambda.Function(stack, 'MyFunction', { | ||
code: lambda.Code.fromInline('exports.handler = async () => {};'), | ||
handler: 'index.handler', | ||
runtime: lambda.Runtime.NODEJS_20_X, | ||
}); | ||
|
||
const fnUrl = fn.addFunctionUrl({ | ||
authType: lambda.FunctionUrlAuthType.NONE, | ||
}); | ||
|
||
const origin = new FunctionUrlOrigin(fnUrl, { | ||
ipAddressType: OriginIpAddressType.DUALSTACK, | ||
}); | ||
|
||
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' }); | ||
|
||
expect(stack.resolve(originBindConfig.originProperty)).toMatchObject({ | ||
customOriginConfig: { | ||
ipAddressType: OriginIpAddressType.DUALSTACK, | ||
}, | ||
}); | ||
}); | ||
|
||
test('Correctly sets ipAddressType to ipv4', () => { | ||
const fn = new lambda.Function(stack, 'MyFunction', { | ||
code: lambda.Code.fromInline('exports.handler = async () => {};'), | ||
handler: 'index.handler', | ||
runtime: lambda.Runtime.NODEJS_20_X, | ||
}); | ||
|
||
const fnUrl = fn.addFunctionUrl({ | ||
authType: lambda.FunctionUrlAuthType.NONE, | ||
}); | ||
|
||
const origin = new FunctionUrlOrigin(fnUrl, { | ||
ipAddressType: OriginIpAddressType.IPV4, | ||
}); | ||
|
||
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' }); | ||
|
||
expect(stack.resolve(originBindConfig.originProperty)).toMatchObject({ | ||
customOriginConfig: { | ||
ipAddressType: OriginIpAddressType.IPV4, | ||
}, | ||
}); | ||
}); | ||
|
||
test('Correctly sets ipAddressType to ipv6', () => { | ||
const fn = new lambda.Function(stack, 'MyFunction', { | ||
code: lambda.Code.fromInline('exports.handler = async () => {};'), | ||
handler: 'index.handler', | ||
runtime: lambda.Runtime.NODEJS_20_X, | ||
}); | ||
|
||
const fnUrl = fn.addFunctionUrl({ | ||
authType: lambda.FunctionUrlAuthType.NONE, | ||
}); | ||
|
||
const origin = new FunctionUrlOrigin(fnUrl, { | ||
ipAddressType: OriginIpAddressType.IPV6, | ||
}); | ||
|
||
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' }); | ||
|
||
expect(stack.resolve(originBindConfig.originProperty)).toMatchObject({ | ||
customOriginConfig: { | ||
ipAddressType: OriginIpAddressType.IPV6, | ||
}, | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is better to aggregate these tests like below.
test('Correctly sets ipAddressType to dualstack', () => { | |
const fn = new lambda.Function(stack, 'MyFunction', { | |
code: lambda.Code.fromInline('exports.handler = async () => {};'), | |
handler: 'index.handler', | |
runtime: lambda.Runtime.NODEJS_20_X, | |
}); | |
const fnUrl = fn.addFunctionUrl({ | |
authType: lambda.FunctionUrlAuthType.NONE, | |
}); | |
const origin = new FunctionUrlOrigin(fnUrl, { | |
ipAddressType: OriginIpAddressType.DUALSTACK, | |
}); | |
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' }); | |
expect(stack.resolve(originBindConfig.originProperty)).toMatchObject({ | |
customOriginConfig: { | |
ipAddressType: OriginIpAddressType.DUALSTACK, | |
}, | |
}); | |
}); | |
test('Correctly sets ipAddressType to ipv4', () => { | |
const fn = new lambda.Function(stack, 'MyFunction', { | |
code: lambda.Code.fromInline('exports.handler = async () => {};'), | |
handler: 'index.handler', | |
runtime: lambda.Runtime.NODEJS_20_X, | |
}); | |
const fnUrl = fn.addFunctionUrl({ | |
authType: lambda.FunctionUrlAuthType.NONE, | |
}); | |
const origin = new FunctionUrlOrigin(fnUrl, { | |
ipAddressType: OriginIpAddressType.IPV4, | |
}); | |
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' }); | |
expect(stack.resolve(originBindConfig.originProperty)).toMatchObject({ | |
customOriginConfig: { | |
ipAddressType: OriginIpAddressType.IPV4, | |
}, | |
}); | |
}); | |
test('Correctly sets ipAddressType to ipv6', () => { | |
const fn = new lambda.Function(stack, 'MyFunction', { | |
code: lambda.Code.fromInline('exports.handler = async () => {};'), | |
handler: 'index.handler', | |
runtime: lambda.Runtime.NODEJS_20_X, | |
}); | |
const fnUrl = fn.addFunctionUrl({ | |
authType: lambda.FunctionUrlAuthType.NONE, | |
}); | |
const origin = new FunctionUrlOrigin(fnUrl, { | |
ipAddressType: OriginIpAddressType.IPV6, | |
}); | |
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' }); | |
expect(stack.resolve(originBindConfig.originProperty)).toMatchObject({ | |
customOriginConfig: { | |
ipAddressType: OriginIpAddressType.IPV6, | |
}, | |
}); | |
}); | |
test.each([OriginIpAddressType.DUALSTACK, OriginIpAddressType.IPV4, OriginIpAddressType.IPV6])('Correctly sets ipAddressType to %s', (ipAddressType) => { | |
const fn = new lambda.Function(stack, 'MyFunction', { | |
code: lambda.Code.fromInline('exports.handler = async () => {};'), | |
handler: 'index.handler', | |
runtime: lambda.Runtime.NODEJS_20_X, | |
}); | |
const fnUrl = fn.addFunctionUrl({ | |
authType: lambda.FunctionUrlAuthType.NONE, | |
}); | |
const origin = new FunctionUrlOrigin(fnUrl, { | |
ipAddressType, | |
}); | |
const originBindConfig = origin.bind(stack, { originId: 'StackOriginLambdaFunctionURL' }); | |
expect(stack.resolve(originBindConfig.originProperty)).toMatchObject({ | |
customOriginConfig: { | |
ipAddressType, | |
}, | |
}); | |
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
absolutely right, I aggregated testcase as you wrote.
/** | ||
* IP address type for CloudFront to use when connecting to the origin. | ||
*/ | ||
export enum OriginIpAddressType { | ||
/** | ||
* CloudFront uses IPv4 only to connect to the origin. | ||
*/ | ||
IPV4 = 'ipv4', | ||
|
||
/** | ||
* CloudFront uses IPv6 only to connect to the origin. | ||
*/ | ||
IPV6 = 'ipv6', | ||
|
||
/** | ||
* CloudFront uses both IPv4 and IPv6 to connect to the origin. | ||
*/ | ||
DUALSTACK = 'dualstack', | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because this enum can be used not only fn url origin but also other origins, it might be better to define this enum in cloudfront/origin.ts
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed it to define this enum in cloudfront/origin.ts.
same as your PR.
i really appreciate your specific feedbacks, I learned a lot !
Could you please fix this lint error and merge the newest main branch? @aws-cdk-testing/framework-integ: /codebuild/output/src419940982/src/actions-runner/_work/aws-cdk/aws-cdk/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront-origins/test/integ.function-url-origin-ip-address-type.ts
@aws-cdk-testing/framework-integ: 58:4 error Newline required at end of file but not found eol-last |
…ature/function-url-origin-ip-address-type
…/github.com/matoom-nomu/aws-cdk into feature/function-url-origin-ip-address-type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Issue # (if applicable)
#35450
Reason for this change
Lambda Function URLs natively support dual-stack IPv4/IPv6 connectivity, but CDK's FunctionUrlOrigin class did not expose the
ipAddressType
property to configure IP protocol preferences.Description of changes
OriginIpAddressType
enum withIPV4
,IPV6
, andDUALSTACK
optionsipAddressType
property toFunctionUrlOriginProps
interface using the enum typeFunctionUrlOrigin
andFunctionUrlOriginWithOAC
classes to pass through the propertyDescribe any new or updated permissions being added
N/A
Description of how you validated changes
ipAddressType
is not specifiedOriginIpAddressType.IPV4
,OriginIpAddressType.IPV6
,OriginIpAddressType.DUALSTACK
)Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license