Skip to content
This repository was archived by the owner on Apr 5, 2025. It is now read-only.

feat(python)!: add v2 layer and drop python 3.6 support #4

Merged
merged 12 commits into from
Oct 3, 2022
Merged
4 changes: 2 additions & 2 deletions .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 @@ -4,7 +4,7 @@ const project = new awscdk.AwsCdkConstructLibrary({
authorUrl: 'https://aws.amazon.com',
authorOrganization: true,
keywords: ['aws', 'cdk', 'powertools', 'python', 'layer', 'lambda', 'devax', 'typescript', 'nodejs'],
cdkVersion: '2.24.1',
cdkVersion: '2.44.0',
defaultReleaseBranch: 'main',
majorVersion: 2,
name: 'cdk-aws-lambda-powertools-layer',
Expand Down
19 changes: 15 additions & 4 deletions API.md

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

29 changes: 19 additions & 10 deletions layer/Python/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
FROM public.ecr.aws/lambda/python:3.8



ARG PACKAGE_SUFFIX=''

USER root
WORKDIR /tmp


# PACKAGE_SUFFIX = '[pydantic]==1.23.0'
# PACKAGE_SUFFIX = '[pydantic]'
# PACKAGE_SUFFIX = '=='1.23.0'
# PACKAGE_SUFFIX = '[all]==2.0.0'
# PACKAGE_SUFFIX = '[all]'
# PACKAGE_SUFFIX = '=='2.0.0'
# PACKAGE_SUFFIX = ''


RUN yum update -y && yum install -y zip unzip wget tar gzip

RUN pip install -t /asset/python aws-lambda-powertools$PACKAGE_SUFFIX
RUN yum update -y && yum install -y zip unzip wget tar gzip binutils

RUN pip install -t /asset/python aws-lambda-powertools$PACKAGE_SUFFIX

# Removing nonessential files
RUN cd /asset && \
# remove boto3 and botocore (already available in Lambda Runtime)
rm -rf python/boto* && \
# remove boto3 dependencies
rm -rf python/s3transfer* python/*dateutil* python/urllib3* && \
# remove debugging symbols
find python -name '*.so' -type f -exec strip "{}" \; && \
# remove tests
find python -wholename "*/tests/*" -type f -delete && \
# remove python bytecode
find python -regex '^.*\(__pycache__\|\.py[co]\)$' -delete
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.

37 changes: 31 additions & 6 deletions src/lambda-powertools-layer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as path from 'path';
import { aws_lambda as lambda } from 'aws-cdk-lib';
import { Architecture } from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';

/**
Expand All @@ -10,8 +11,9 @@ export interface PowertoolsLayerProps {
* The powertools package version from pypi repository.
*/
readonly version?: string;

/**
* A flag for the pydantic extras dependency, used for parsing.
* A flag for the extras dependencies (pydantic, aws-xray-sdk, etc.)
* This will increase the size of the layer significantly. If you don't use parsing, ignore it.
*/
readonly includeExtras?: boolean;
Expand All @@ -25,6 +27,11 @@ export interface PowertoolsLayerProps {
* the runtime of the layer
*/
readonly runtimeFamily?: lambda.RuntimeFamily;

/**
* The compatible architectures for the layer
*/
readonly compatibleArchitectures?: lambda.Architecture[];
}

/**
Expand All @@ -36,7 +43,7 @@ export class LambdaPowertoolsLayer extends lambda.LayerVersion {
* There are multiple combinations between version and extras package that results in different suffix for the installation.
* With and without version, with and without extras flag.
* We construct one suffix here because it is easier to do in code than inside the Dockerfile with bash commands.
* For example, if we set extras=true and version=1.22.0 we get '[pydantic]==1.22.0'.
* For example, if we set `includeExtras=true` and `version=1.22.0` we get '[all]==1.22.0'.
*
*/
static constructBuildArgs(
Expand All @@ -48,7 +55,7 @@ export class LambdaPowertoolsLayer extends lambda.LayerVersion {
switch (runtimeFamily) {
case lambda.RuntimeFamily.PYTHON:
if (includeExtras) {
suffix = '[pydantic]';
suffix = '[all]';
}
if (version) {
suffix = `${suffix}==${version}`;
Expand All @@ -69,6 +76,9 @@ 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(', ');

console.log(`path ${dockerFilePath}`);
super(scope, id, {
code: lambda.Code.fromDockerBuild(dockerFilePath, {
Expand All @@ -79,12 +89,15 @@ export class LambdaPowertoolsLayer extends lambda.LayerVersion {
props?.version,
),
},
// supports cross-platform docker build
platform: getDockerPlatformNameFromArchitectures(compatibleArchitectures),
}),
layerVersionName: props?.layerVersionName ? props?.layerVersionName : undefined,
license: 'MIT-0',
compatibleRuntimes: getRuntimesFromRuntimeFamily(runtimeFamily),
description: `Lambda Powertools for ${languageName}${
props?.includeExtras ? ' with Pydantic' : ''
compatibleArchitectures,
description: `Lambda Powertools for ${languageName} [${compatibleArchitecturesDescription}]${
props?.includeExtras ? ' with extra dependencies' : ''
} ${props?.version ? `version ${props?.version}` : 'latest version'}`.trim(),
});
}
Expand All @@ -94,7 +107,6 @@ function getRuntimesFromRuntimeFamily(runtimeFamily: lambda.RuntimeFamily): lamb
switch (runtimeFamily) {
case lambda.RuntimeFamily.PYTHON:
return [
lambda.Runtime.PYTHON_3_6,
lambda.Runtime.PYTHON_3_7,
lambda.Runtime.PYTHON_3_8,
lambda.Runtime.PYTHON_3_9,
Expand All @@ -120,3 +132,16 @@ function getLanguageNameFromRuntimeFamily(runtimeFamily: lambda.RuntimeFamily):
return 'Unknown';
}
}

// Docker expects a single string for the --platform option.
// getDockerPlatformNameFromArchitectures converts the Architecture enum to a string.
function getDockerPlatformNameFromArchitectures(architectures: lambda.Architecture[]): string {
if (architectures.length == 1) {
return architectures[0].dockerPlatform;
} else {
// if we have multiple architectures, we default to x86_64, hoping for the
// layer not to have any architecture specific code or at least contain
// binary code for all architectures
return Architecture.X86_64.dockerPlatform;
}
}
36 changes: 25 additions & 11 deletions test/lambda-powertools-python-layer.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Stack } from 'aws-cdk-lib';
import { Template } from 'aws-cdk-lib/assertions';
import { RuntimeFamily } from 'aws-cdk-lib/aws-lambda';
import { Architecture, RuntimeFamily } from 'aws-cdk-lib/aws-lambda';
import { LambdaPowertoolsLayer } from '../src';


Expand All @@ -10,7 +10,7 @@ describe('with no configuration the construct', () => {
const template = Template.fromStack(stack);
test('synthesizes successfully', () => {
template.hasResourceProperties('AWS::Lambda::LayerVersion', {
Description: 'Lambda Powertools for Python latest version',
Description: 'Lambda Powertools for Python [x86_64] latest version',
});
});

Expand All @@ -23,7 +23,6 @@ describe('with no configuration the construct', () => {
test('matches the python 3.x runtimes', () => {
template.hasResourceProperties('AWS::Lambda::LayerVersion', {
CompatibleRuntimes: [
'python3.6',
'python3.7',
'python3.8',
'python3.9',
Expand All @@ -32,6 +31,21 @@ describe('with no configuration the construct', () => {
});
});

describe('with arm64 architecture', () => {
const stack = new Stack();
new LambdaPowertoolsLayer(stack, 'PowertoolsLayer', {
runtimeFamily: RuntimeFamily.PYTHON,
compatibleArchitectures: [Architecture.ARM_64],
});
const template = Template.fromStack(stack);
test('synthesizes successfully', () => {
template.hasResourceProperties('AWS::Lambda::LayerVersion', {
Description: 'Lambda Powertools for Python [arm64] latest version',
CompatibleArchitectures: ['arm64'],
});
});
});

describe('for layerVersionName configuraiton the construct', () => {
test('synthisizes to a layer with provided name', () => {
const stack = new Stack();
Expand All @@ -54,7 +68,7 @@ describe('with version configuration the construct', () => {


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

Expand All @@ -73,34 +87,34 @@ describe('with version configuration the construct', () => {
});

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

});

test('synthesizes with pyndatic and latest version', () => {
test('synthesizes with extras and latest version', () => {
const stack = new Stack();
new LambdaPowertoolsLayer(stack, 'LayerExtrasNoVersion', {
includeExtras: true,
});

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

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

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

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

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

test('returns only version when no extras flag provided', () => {
Expand Down
4 changes: 2 additions & 2 deletions test/lambda-powertools-typescript-layer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('with minimal configuration the construct', () => {
const template = Template.fromStack(stack);
test('synthesizes successfully', () => {
template.hasResourceProperties('AWS::Lambda::LayerVersion', {
Description: 'Lambda Powertools for TypeScript latest version',
Description: 'Lambda Powertools for TypeScript [x86_64] latest version',
});
});

Expand Down Expand Up @@ -57,7 +57,7 @@ describe('with version configuration the construct', () => {


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

Expand Down
12 changes: 6 additions & 6 deletions yarn.lock

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