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

feat: add datasource for AWS Lambda Layers #28251

Open
wants to merge 86 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 82 commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
1e07577
add dummy datasource description for draft pull request only
kayman-mk Nov 22, 2022
fa638fe
base implementation
kayman-mk Nov 24, 2022
82652f3
add the code
kayman-mk Nov 24, 2022
7a9a5d0
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Nov 29, 2022
9edb2e0
retrieve layer list from AWS
kayman-mk Nov 29, 2022
a6168f2
add tests
kayman-mk Nov 29, 2022
f25dad7
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Nov 30, 2022
82f8849
Update lib/modules/datasource/aws-versioned-arn/readme.md
kayman-mk Jan 7, 2023
e51647a
reset package manager
kayman-mk Jan 7, 2023
5978e15
Merge remote-tracking branch 'origin/main' into kayma/add-aws-version…
kayman-mk Jan 7, 2023
3ca698d
add aws-sdk lambda
kayman-mk Jan 7, 2023
7b5ed71
add datasource to API
kayman-mk Jan 7, 2023
1162613
add docs
kayman-mk Jan 7, 2023
c2ffdc5
files lost by accident
kayman-mk Jan 8, 2023
d2b2d07
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Jan 8, 2023
87d1fa0
files lost by accident
kayman-mk Jan 8, 2023
9230bb9
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Jan 9, 2023
4999cc2
Merge branch 'kayma/add-aws-versioned-arn-datasource' of github.com:H…
kayman-mk Jan 14, 2023
4f24402
rename files
kayman-mk Jan 14, 2023
1209738
add tests
kayman-mk Jan 14, 2023
e2cfb26
add tests
kayman-mk Jan 14, 2023
5e3dad2
add tests
kayman-mk Jan 15, 2023
6c4a7e0
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Jan 15, 2023
ad2bbbc
fix tests and config
kayman-mk Jan 15, 2023
1dda08d
remove sorting
kayman-mk Jan 15, 2023
2f02403
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Jan 18, 2023
10fbbcd
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Jan 18, 2023
9c61e5d
Merge remote-tracking branch 'renovate/main' into kayma/add-aws-versi…
kayman-mk Apr 5, 2024
fe88ea6
add docs
kayman-mk Apr 5, 2024
66fa69e
fix compile error
kayman-mk Apr 6, 2024
4b9e51b
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Apr 10, 2024
02f9af0
merge main
kayman-mk Aug 8, 2024
9240b21
fix review
kayman-mk Aug 8, 2024
c34473a
Merge remote-tracking branch 'origin/main' into kayma/add-aws-version…
kayman-mk Aug 8, 2024
b41aa9d
fix tests and rename `name` to `arn` (in filter)
kayman-mk Aug 8, 2024
673a5d3
ADD-AWS-VERSIONED-ARN-DATASOURCE
kayman-mk Aug 8, 2024
f2d87ee
fix docs
kayman-mk Aug 8, 2024
83d626a
add tests
kayman-mk Aug 10, 2024
edf9814
make architecture and runtime optional
kayman-mk Aug 10, 2024
cbf0b4e
fix example in readme
kayman-mk Aug 10, 2024
afe1dab
simplify regex
kayman-mk Aug 10, 2024
f4f9251
fix regex in example
kayman-mk Aug 10, 2024
4515246
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Aug 10, 2024
5c5a961
use customManagers in config
kayman-mk Aug 10, 2024
8d075fd
adding customType to config
kayman-mk Aug 10, 2024
8bd1eef
run prettier
kayman-mk Aug 10, 2024
81b4a5c
fix tests
kayman-mk Aug 10, 2024
be3e691
remove unused import
kayman-mk Aug 10, 2024
44b60c3
ADD-AWS-VERSIONED-ARN-DATASOURCE
kayman-mk Aug 11, 2024
ff1d7f0
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Aug 11, 2024
5babf77
run prettier
kayman-mk Aug 11, 2024
30e0d37
Merge branch 'kayma/add-aws-versioned-arn-datasource' of https://gith…
kayman-mk Aug 11, 2024
c94102d
add tests
kayman-mk Aug 11, 2024
39c7e3e
ADD-AWS-VERSIONED-ARN-DATASOURCE
kayman-mk Aug 11, 2024
592ebd9
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Aug 12, 2024
8cf8382
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Aug 12, 2024
0350e8b
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Aug 12, 2024
579e321
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Aug 13, 2024
e5a42f5
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Aug 13, 2024
46d9fc6
fix pnpm-lock
kayman-mk Aug 15, 2024
93eaf1e
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Aug 15, 2024
2bc07b8
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Aug 15, 2024
045c1f8
Merge branch 'main' into kayma/add-aws-versioned-arn-datasource
kayman-mk Aug 16, 2024
f62203a
inline fixtures
kayman-mk Aug 22, 2024
a7198b4
Update lib/modules/datasource/aws-lambda-layer/schema.ts
kayman-mk Aug 22, 2024
1673107
merge main
kayman-mk Aug 22, 2024
1bc7563
add tests
kayman-mk Aug 22, 2024
6d0c583
Merge remote-tracking branch 'origin/main' into kayma/add-aws-version…
kayman-mk Aug 22, 2024
579fd66
update pnpm-lock.yaml
kayman-mk Aug 22, 2024
718c9ff
fix api.ts
kayman-mk Aug 22, 2024
a373631
add warning if runtime/architecture not filtered but returned by AWS
kayman-mk Aug 24, 2024
ebc0f4c
fix merge conflict in lock file
kayman-mk Aug 24, 2024
41849bc
add tests for logger warning
kayman-mk Aug 24, 2024
cc4e844
Update lib/modules/datasource/aws-lambda-layer/index.spec.ts
kayman-mk Aug 27, 2024
3c37e25
fix review
kayman-mk Aug 27, 2024
981cb1b
Merge branch 'kayma/add-aws-versioned-arn-datasource' of https://gith…
kayman-mk Aug 27, 2024
aee4443
lock file
kayman-mk Aug 27, 2024
08def38
lock file
kayman-mk Aug 27, 2024
7d84a21
fix tests
kayman-mk Aug 27, 2024
bf886ff
fix logger
kayman-mk Aug 27, 2024
0641910
update package lock
kayman-mk Aug 29, 2024
44b5529
fix test
kayman-mk Aug 29, 2024
902c592
fix prettier
kayman-mk Aug 29, 2024
c81eb0d
log error as error and ignore for tests
kayman-mk Aug 29, 2024
645d5bc
run prettier
kayman-mk Aug 31, 2024
df54fb4
istanbul ignore if
kayman-mk Aug 31, 2024
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
2 changes: 2 additions & 0 deletions lib/modules/datasource/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ArtifactoryDatasource } from './artifactory';
import { AwsLambdaLayerDataSource } from './aws-lambda-layer';
import { AwsMachineImageDatasource } from './aws-machine-image';
import { AwsRdsDatasource } from './aws-rds';
import { AzureBicepResourceDatasource } from './azure-bicep-resource';
Expand Down Expand Up @@ -68,6 +69,7 @@ const api = new Map<string, DatasourceApi>();
export default api;

api.set(ArtifactoryDatasource.id, new ArtifactoryDatasource());
api.set(AwsLambdaLayerDataSource.id, new AwsLambdaLayerDataSource());
api.set(AwsMachineImageDatasource.id, new AwsMachineImageDatasource());
api.set(AwsRdsDatasource.id, new AwsRdsDatasource());
api.set(AzureBicepResourceDatasource.id, new AzureBicepResourceDatasource());
Expand Down
329 changes: 329 additions & 0 deletions lib/modules/datasource/aws-lambda-layer/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,329 @@
import type {
LayerVersionsListItem,
ListLayerVersionsCommandOutput} from '@aws-sdk/client-lambda';
import {
LambdaClient,
ListLayerVersionsCommand
} from '@aws-sdk/client-lambda';
import { mockClient } from 'aws-sdk-client-mock';
import { getPkgReleases } from '..';
import { logger } from '../../../logger';
import { AwsLambdaLayerDataSource } from '.';

const datasource = AwsLambdaLayerDataSource.id;

/**
* Testdata for mock implementation of LambdaClient
* layer1 to layer3 from oldest to newest
*/
const layer1: LayerVersionsListItem = {
Version: 1,
CreatedDate: '2021-01-10T00:00:00.000Z',
LayerVersionArn: 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer:3',
};

const layer2: LayerVersionsListItem = {
Version: 2,
CreatedDate: '2021-02-05T00:00:00.000Z',
LayerVersionArn: 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer:2',
};

const layer3: LayerVersionsListItem = {
Version: 3,
CreatedDate: '2021-03-01T00:00:00.000Z',
LayerVersionArn: 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer:1',
};

const layerWithArchitecture: LayerVersionsListItem = {
Version: 3,
CreatedDate: '2021-03-01T00:00:00.000Z',
LayerVersionArn: 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer:1',
CompatibleArchitectures: ['x86_64'],
};

const layerWithRuntime: LayerVersionsListItem = {
Version: 3,
CreatedDate: '2021-03-01T00:00:00.000Z',
LayerVersionArn: 'arn:aws:lambda:us-east-1:123456789012:layer:my-layer:1',
CompatibleRuntimes: ['python3.7'],
};

const mock3Layers: ListLayerVersionsCommandOutput = {
LayerVersions: [layer1, layer2, layer3],
$metadata: {},
};

const mock1Layer: ListLayerVersionsCommandOutput = {
LayerVersions: [layer3],
$metadata: {},
};

const mockEmpty: ListLayerVersionsCommandOutput = {
LayerVersions: [],
$metadata: {},
};

const mockLayerWithArchitecture: ListLayerVersionsCommandOutput = {
LayerVersions: [layerWithArchitecture],
$metadata: {},
};

const mockLayerWithRuntime: ListLayerVersionsCommandOutput = {
LayerVersions: [layerWithRuntime],
$metadata: {},
};

const lambdaClientMock = mockClient(LambdaClient);

function mockListLayerVersionsCommandOutput(
result: ListLayerVersionsCommandOutput,
): void {
lambdaClientMock.reset();
lambdaClientMock.on(ListLayerVersionsCommand).resolves(result);
}

describe('modules/datasource/aws-lambda-layer/index', () => {
describe('getSortedLambdaLayerVersions', () => {
it('should warn about missing architecture in filter if AWS response contains architecture', async () => {
mockListLayerVersionsCommandOutput(mockLayerWithArchitecture);
const lambdaLayerDatasource = new AwsLambdaLayerDataSource();

await lambdaLayerDatasource.getSortedLambdaLayerVersions(
'arn',
'runtime',
undefined,
);

expect(logger.warn).toHaveBeenCalledWith(
'AWS returned layers with architecture but the architecture is not set in the filter. You might update to a layer with wrong architecture.',
);
});

it('should warn about missing runtime in filter if AWS response contains runtime', async () => {
mockListLayerVersionsCommandOutput(mockLayerWithRuntime);
const lambdaLayerDatasource = new AwsLambdaLayerDataSource();

await lambdaLayerDatasource.getSortedLambdaLayerVersions(
'arn',
undefined,
'architecture',
);

expect(logger.warn).toHaveBeenCalledWith(
'AWS returned layers with runtime but the runtime is not set in the filter. You might update to a layer with wrong runtime.',
);
});

it('should return empty array if no layers are found', async () => {
mockListLayerVersionsCommandOutput(mockEmpty);
const lamdbaLayerDatasource = new AwsLambdaLayerDataSource();
const res = await lamdbaLayerDatasource.getSortedLambdaLayerVersions(
'xy',
'arm64',
'python3.8',
);

expect(res).toEqual([]);
});

it('should return an empty array if AWS response does not contain LayerVersions', async () => {
const lambdaLayerDatasource = new AwsLambdaLayerDataSource();
lambdaClientMock.on(ListLayerVersionsCommand).resolves({
$metadata: {},
});

const res = await lambdaLayerDatasource.getSortedLambdaLayerVersions(
'xy',
'arm64',
'python3.8',
);

expect(res).toEqual([]);
});

it('should return array with one layer if one layer found', async () => {
mockListLayerVersionsCommandOutput(mock1Layer);
const lamdbaLayerDatasource = new AwsLambdaLayerDataSource();
const res = await lamdbaLayerDatasource.getSortedLambdaLayerVersions(
'not-relevant',
'not-relevant',
'not-relevant',
);

expect(res).toEqual([layer3]);
});

it('should return array with three layers if three layers found', async () => {
mockListLayerVersionsCommandOutput(mock3Layers);
const lamdbaLayerDatasource = new AwsLambdaLayerDataSource();
const res = await lamdbaLayerDatasource.getSortedLambdaLayerVersions(
'not-relevant',
'not-relevant',
'not-relevant',
);

expect(res).toEqual([layer1, layer2, layer3]);
});

it('should pass the filters for listLayerVersions when calling the AWS API', async () => {
mockListLayerVersionsCommandOutput(mock3Layers);
const lambdaLayerDatasource = new AwsLambdaLayerDataSource();

await lambdaLayerDatasource.getSortedLambdaLayerVersions(
'arn',
'runtime',
'architecture',
);

expect(lambdaClientMock.calls()).toHaveLength(1);

expect(lambdaClientMock.calls()[0].args[0].input).toEqual({
CompatibleArchitecture: 'architecture',
CompatibleRuntime: 'runtime',
LayerName: 'arn',
});
});

it('should call AWS with no architecture if there is no architecture in the filter', async () => {
mockListLayerVersionsCommandOutput(mock3Layers);
const lambdaLayerDatasource = new AwsLambdaLayerDataSource();

await lambdaLayerDatasource.getSortedLambdaLayerVersions(
'arn',
'runtime',
undefined,
);

expect(lambdaClientMock.calls()).toHaveLength(1);

expect(lambdaClientMock.calls()[0].args[0].input).toEqual({
CompatibleArchitecture: undefined,
CompatibleRuntime: 'runtime',
LayerName: 'arn',
});
});

it('should call AWS with no runtime if there is no runtime in the filter', async () => {
mockListLayerVersionsCommandOutput(mock3Layers);
const lambdaLayerDatasource = new AwsLambdaLayerDataSource();

await lambdaLayerDatasource.getSortedLambdaLayerVersions(
'arn',
undefined,
'python3.8',
);

expect(lambdaClientMock.calls()).toHaveLength(1);

expect(lambdaClientMock.calls()[0].args[0].input).toEqual({
CompatibleArchitecture: 'python3.8',
CompatibleRuntime: undefined,
LayerName: 'arn',
});
});
});

describe('getReleases', () => {
it('should throw an exception if the filter criteria does not match the schema', async () => {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@viceice Is this assumption correct? If not: how to catch this error?

try {
  FilterParser.parse(xxxx);
} catch (e) {
  // log error and return null
}

Looks ugly and I guess there is a better option. Checking the existing source, I didn't find any.

const lambdaLayerDatasource = new AwsLambdaLayerDataSource();
const res = lambdaLayerDatasource.getReleases({
packageName: '{"invalid": "json"}',
});

await expect(res).rejects.toThrow();
});

it('should throw an exception if version is not set in AWS response', async () => {
mockListLayerVersionsCommandOutput({
LayerVersions: [
{
CreatedDate: '2021-01-10T00:00:00.000Z',
LayerVersionArn:
'arn:aws:lambda:us-east-1:123456789012:layer:my-layer:3',
},
],
$metadata: {},
});
const lambdaLayerDatasource = new AwsLambdaLayerDataSource();

await expect(() =>
lambdaLayerDatasource.getReleases({
packageName:
'{"arn": "arn:aws:lambda:us-east-1:123456789012:layer:my-layer", "runtime": "python37", "architecture": "x86_64"}',
}),
).rejects.toThrow(
'Version is not set in AWS response for ListLayerVersionsCommand',
);
});
});

describe('integration', () => {
describe('getPkgReleases', () => {
it('should return null if no releases found', async () => {
mockListLayerVersionsCommandOutput(mockEmpty);

const res = await getPkgReleases({
datasource,
packageName:
'{"arn": "arn:aws:lambda:us-east-1:123456789012:layer:my-layer", "runtime": "python37", "architecture": "x86_64"}',
});

expect(res).toBeNull();
});

it('should return one image', async () => {
mockListLayerVersionsCommandOutput(mock1Layer);

const res = await getPkgReleases({
datasource,
packageName:
'{"arn": "arn:aws:lambda:us-east-1:123456789012:layer:my-layer", "runtime": "python37", "architecture": "x86_64"}',
});

expect(res).toStrictEqual({
releases: [
{
isDeprecated: false,
version: layer3.Version?.toFixed(0),
newDigest: layer3.LayerVersionArn,
releaseTimestamp: layer3.CreatedDate,
},
],
});
});

it('should return 3 images', async () => {
mockListLayerVersionsCommandOutput(mock3Layers);

const res = await getPkgReleases({
datasource,
packageName:
'{"arn": "arn:aws:lambda:us-east-1:123456789012:layer:my-layer", "runtime": "python37", "architecture": "x86_64"}',
});

expect(res).toStrictEqual({
releases: [
{
isDeprecated: false,
version: layer1.Version?.toFixed(0),
newDigest: layer1.LayerVersionArn,
releaseTimestamp: layer1.CreatedDate,
},
{
isDeprecated: false,
version: layer2.Version?.toFixed(0),
newDigest: layer2.LayerVersionArn,
releaseTimestamp: layer2.CreatedDate,
},
{
isDeprecated: false,
version: layer3.Version?.toFixed(0),
newDigest: layer3.LayerVersionArn,
releaseTimestamp: layer3.CreatedDate,
},
],
});
});
});
});
});
Loading
Loading