Skip to content

Commit

Permalink
feat: add support for Python 3.12 and Node 20 (#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenfonseca authored Nov 15, 2023
1 parent 32d1e90 commit 8b7c989
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 131 deletions.
2 changes: 1 addition & 1 deletion .projen/deps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .projenrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const project = new awscdk.AwsCdkConstructLibrary({
'typescript',
'nodejs',
],
cdkVersion: '2.88.0',
cdkVersion: '2.108.1',
defaultReleaseBranch: 'main',
minNodeVersion: '16.19.1',
majorVersion: 3,
Expand Down
4 changes: 2 additions & 2 deletions package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 33 additions & 11 deletions src/lambda-powertools-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,12 @@ export class LambdaPowertoolsLayer extends lambda.LayerVersion {
const runtimeFamily = props?.runtimeFamily ?? lambda.RuntimeFamily.PYTHON;
const languageName = getLanguageNameFromRuntimeFamily(runtimeFamily);
const dockerFilePath = path.join(__dirname, `../layer/${languageName}`);
const compatibleArchitectures = props?.compatibleArchitectures ?? [lambda.Architecture.X86_64];
const compatibleArchitecturesDescription = compatibleArchitectures.map((arch) => arch.name).join(', ');
const compatibleArchitectures = props?.compatibleArchitectures ?? [
lambda.Architecture.X86_64,
];
const compatibleArchitecturesDescription = compatibleArchitectures
.map((arch) => arch.name)
.join(', ');

console.log(`path ${dockerFilePath}`);
super(scope, id, {
Expand All @@ -94,23 +98,35 @@ export class LambdaPowertoolsLayer extends lambda.LayerVersion {
),
},
// supports cross-platform docker build
platform: getDockerPlatformNameFromArchitectures(compatibleArchitectures),
platform: getDockerPlatformNameFromArchitectures(
compatibleArchitectures,
),
}),
layerVersionName: props?.layerVersionName ? props?.layerVersionName : undefined,
layerVersionName: props?.layerVersionName
? props?.layerVersionName
: undefined,
license: 'MIT-0',
compatibleRuntimes: getRuntimesFromRuntimeFamily(runtimeFamily),
description: `Powertools for AWS Lambda (${languageName}) [${compatibleArchitecturesDescription}]${
props?.includeExtras ? ' with extra dependencies' : ''
} ${props?.version ? `version ${props?.version}` : 'latest version'}`.trim(),
description:
`Powertools for AWS Lambda (${languageName}) [${compatibleArchitecturesDescription}]${
props?.includeExtras ? ' with extra dependencies' : ''
} ${
props?.version ? `version ${props?.version}` : 'latest version'
}`.trim(),
// Dear reader: I'm happy that you've stumbled upon this line too! You might wonder, why are we doing this and passing `undefined` when the list is empty?
// Answer: on regions that don't support ARM64 Lambdas, we can't use the `compatibleArchitectures` parameter. Otherwise CloudFormation will bail with an error.
// So if this construct is called with en empty list of architectures, just pass undefined down to the CDK construct.
compatibleArchitectures: compatibleArchitectures.length == 0 ? undefined : compatibleArchitectures,
compatibleArchitectures:
compatibleArchitectures.length == 0
? undefined
: compatibleArchitectures,
});
}
}

function getRuntimesFromRuntimeFamily(runtimeFamily: lambda.RuntimeFamily): lambda.Runtime[] | undefined {
function getRuntimesFromRuntimeFamily(
runtimeFamily: lambda.RuntimeFamily,
): lambda.Runtime[] | undefined {
switch (runtimeFamily) {
case lambda.RuntimeFamily.PYTHON:
return [
Expand All @@ -119,20 +135,24 @@ function getRuntimesFromRuntimeFamily(runtimeFamily: lambda.RuntimeFamily): lamb
lambda.Runtime.PYTHON_3_9,
lambda.Runtime.PYTHON_3_10,
lambda.Runtime.PYTHON_3_11,
lambda.Runtime.PYTHON_3_12,
];
case lambda.RuntimeFamily.NODEJS:
return [
lambda.Runtime.NODEJS_12_X,
lambda.Runtime.NODEJS_14_X,
lambda.Runtime.NODEJS_16_X,
lambda.Runtime.NODEJS_18_X,
lambda.Runtime.NODEJS_20_X,
];
default:
return [];
}
}

function getLanguageNameFromRuntimeFamily(runtimeFamily: lambda.RuntimeFamily): string {
function getLanguageNameFromRuntimeFamily(
runtimeFamily: lambda.RuntimeFamily,
): string {
switch (runtimeFamily) {
case lambda.RuntimeFamily.PYTHON:
return 'Python';
Expand All @@ -145,7 +165,9 @@ function getLanguageNameFromRuntimeFamily(runtimeFamily: lambda.RuntimeFamily):

// Docker expects a single string for the --platform option.
// getDockerPlatformNameFromArchitectures converts the Architecture enum to a string.
function getDockerPlatformNameFromArchitectures(architectures: lambda.Architecture[]): string {
function getDockerPlatformNameFromArchitectures(
architectures: lambda.Architecture[],
): string {
if (architectures.length == 1) {
return architectures[0].dockerPlatform;
} else {
Expand Down
95 changes: 68 additions & 27 deletions test/lambda-powertools-python-layer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Template } from 'aws-cdk-lib/assertions';
import { Architecture, RuntimeFamily } from 'aws-cdk-lib/aws-lambda';
import { LambdaPowertoolsLayer } from '../src';


describe('with no configuration the construct', () => {
const stack = new Stack();
new LambdaPowertoolsLayer(stack, 'PowertoolsLayer');
Expand All @@ -28,6 +27,7 @@ describe('with no configuration the construct', () => {
'python3.9',
'python3.10',
'python3.11',
'python3.12',
],
});
});
Expand Down Expand Up @@ -55,9 +55,12 @@ describe('for layerVersionName configuraiton the construct', () => {
layerVersionName: 'mySpecialName',
});

Template.fromStack(stack).hasResourceProperties('AWS::Lambda::LayerVersion', {
LayerName: 'mySpecialName',
});
Template.fromStack(stack).hasResourceProperties(
'AWS::Lambda::LayerVersion',
{
LayerName: 'mySpecialName',
},
);
});
});

Expand All @@ -68,17 +71,23 @@ describe('with version configuration the construct', () => {
version: '1.21.0',
});


Template.fromStack(stack).hasResourceProperties('AWS::Lambda::LayerVersion', {
Description: 'Powertools for AWS Lambda (Python) [x86_64] version 1.21.0',
});
Template.fromStack(stack).hasResourceProperties(
'AWS::Lambda::LayerVersion',
{
Description:
'Powertools for AWS Lambda (Python) [x86_64] version 1.21.0',
},
);
});

test('fails with invalid version', () => {
const stack = new Stack();
expect(() => new LambdaPowertoolsLayer(stack, 'PowertoolsLayerBadVersion', {
version: '0.0.0',
})).toThrow(/docker exited with status 1/);
expect(
() =>
new LambdaPowertoolsLayer(stack, 'PowertoolsLayerBadVersion', {
version: '0.0.0',
}),
).toThrow(/docker exited with status 1/);
});

test('synthesizes with pynadtic and specific version', () => {
Expand All @@ -88,10 +97,13 @@ describe('with version configuration the construct', () => {
version: '1.22.0',
});

Template.fromStack(stack).hasResourceProperties('AWS::Lambda::LayerVersion', {
Description: 'Powertools for AWS Lambda (Python) [x86_64] with extra dependencies version 1.22.0',
});

Template.fromStack(stack).hasResourceProperties(
'AWS::Lambda::LayerVersion',
{
Description:
'Powertools for AWS Lambda (Python) [x86_64] with extra dependencies version 1.22.0',
},
);
});

test('synthesizes with extras and latest version', () => {
Expand All @@ -100,49 +112,78 @@ describe('with version configuration the construct', () => {
includeExtras: true,
});

Template.fromStack(stack).hasResourceProperties('AWS::Lambda::LayerVersion', {
Description: 'Powertools for AWS Lambda (Python) [x86_64] with extra dependencies latest version',
});
Template.fromStack(stack).hasResourceProperties(
'AWS::Lambda::LayerVersion',
{
Description:
'Powertools for AWS Lambda (Python) [x86_64] with extra dependencies latest version',
},
);
});
});

describe('construct build args for Dockerfile', () => {
test('returns extras and version', () => {
const args = LambdaPowertoolsLayer.constructBuildArgs(RuntimeFamily.PYTHON, true, '1.21.0');
const args = LambdaPowertoolsLayer.constructBuildArgs(
RuntimeFamily.PYTHON,
true,
'1.21.0',
);

expect(args).toEqual('[all]==1.21.0');
});

test('returns only extras when no version provided', () => {
const args = LambdaPowertoolsLayer.constructBuildArgs(RuntimeFamily.PYTHON, true, undefined);
const args = LambdaPowertoolsLayer.constructBuildArgs(
RuntimeFamily.PYTHON,
true,
undefined,
);

expect(args).toEqual('[all]');
});

test('returns a git url with extras when a git url is provided', () => {
const version = 'git+https://github.com/awslabs/aws-lambda-powertools-python@v2';
const args = LambdaPowertoolsLayer.constructBuildArgs(RuntimeFamily.PYTHON, true, version);
const version =
'git+https://github.com/awslabs/aws-lambda-powertools-python@v2';
const args = LambdaPowertoolsLayer.constructBuildArgs(
RuntimeFamily.PYTHON,
true,
version,
);

expect(args).toEqual(`[all] @ ${version}`);
});

test('returns only version when no extras flag provided', () => {
const args = LambdaPowertoolsLayer.constructBuildArgs(RuntimeFamily.PYTHON, undefined, '1.11.0');
const args = LambdaPowertoolsLayer.constructBuildArgs(
RuntimeFamily.PYTHON,
undefined,
'1.11.0',
);

expect(args).toEqual('==1.11.0');
});

test('returns empty when no version and extras provided', () => {
const args = LambdaPowertoolsLayer.constructBuildArgs(RuntimeFamily.PYTHON, undefined, undefined);
const args = LambdaPowertoolsLayer.constructBuildArgs(
RuntimeFamily.PYTHON,
undefined,
undefined,
);

expect(args).toEqual('');
});

test('returns a git url when a git url is provided and extras provided', () => {
const version = 'git+https://github.com/awslabs/aws-lambda-powertools-python@v2';
const args = LambdaPowertoolsLayer.constructBuildArgs(RuntimeFamily.PYTHON, false, version);
const version =
'git+https://github.com/awslabs/aws-lambda-powertools-python@v2';
const args = LambdaPowertoolsLayer.constructBuildArgs(
RuntimeFamily.PYTHON,
false,
version,
);

expect(args).toEqual(` @ ${version}`);
});

});
49 changes: 32 additions & 17 deletions test/lambda-powertools-typescript-layer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Template } from 'aws-cdk-lib/assertions';
import { RuntimeFamily } from 'aws-cdk-lib/aws-lambda';
import { LambdaPowertoolsLayer } from '../src';


describe('with minimal configuration the construct', () => {
const stack = new Stack();
new LambdaPowertoolsLayer(stack, 'PowertoolsLayer', {
Expand All @@ -12,7 +11,8 @@ describe('with minimal configuration the construct', () => {
const template = Template.fromStack(stack);
test('synthesizes successfully', () => {
template.hasResourceProperties('AWS::Lambda::LayerVersion', {
Description: 'Powertools for AWS Lambda (TypeScript) [x86_64] latest version',
Description:
'Powertools for AWS Lambda (TypeScript) [x86_64] latest version',
});
});

Expand All @@ -29,6 +29,7 @@ describe('with minimal configuration the construct', () => {
'nodejs14.x',
'nodejs16.x',
'nodejs18.x',
'nodejs20.x',
],
});
});
Expand All @@ -41,9 +42,12 @@ describe('for layerVersionName configuration the construct', () => {
layerVersionName: 'mySpecialName',
});

Template.fromStack(stack).hasResourceProperties('AWS::Lambda::LayerVersion', {
LayerName: 'mySpecialName',
});
Template.fromStack(stack).hasResourceProperties(
'AWS::Lambda::LayerVersion',
{
LayerName: 'mySpecialName',
},
);
});
});

Expand All @@ -56,31 +60,42 @@ describe('with version configuration the construct', () => {
version: version,
});


Template.fromStack(stack).hasResourceProperties('AWS::Lambda::LayerVersion', {
Description: `Powertools for AWS Lambda (TypeScript) [x86_64] version ${version}`,
});
Template.fromStack(stack).hasResourceProperties(
'AWS::Lambda::LayerVersion',
{
Description: `Powertools for AWS Lambda (TypeScript) [x86_64] version ${version}`,
},
);
});

test('fails with invalid version', () => {
const stack = new Stack();
expect(() => new LambdaPowertoolsLayer(stack, 'PowertoolsLayerBadVersion', {
runtimeFamily: RuntimeFamily.NODEJS,
version: '12.222.21123',
})).toThrow();
expect(
() =>
new LambdaPowertoolsLayer(stack, 'PowertoolsLayerBadVersion', {
runtimeFamily: RuntimeFamily.NODEJS,
version: '12.222.21123',
}),
).toThrow();
});


test('returns version with @ when provided provided', () => {
const args = LambdaPowertoolsLayer.constructBuildArgs(RuntimeFamily.NODEJS, undefined, '0.9.0');
const args = LambdaPowertoolsLayer.constructBuildArgs(
RuntimeFamily.NODEJS,
undefined,
'0.9.0',
);

expect(args).toEqual('@0.9.0');
});

test('returns empty when no version provided', () => {
const args = LambdaPowertoolsLayer.constructBuildArgs(RuntimeFamily.NODEJS, undefined, undefined);
const args = LambdaPowertoolsLayer.constructBuildArgs(
RuntimeFamily.NODEJS,
undefined,
undefined,
);

expect(args).toEqual('');
});

});
Loading

0 comments on commit 8b7c989

Please sign in to comment.