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: implement aws-eks-addon datasource #29613

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
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 { AwsEKSAddonDataSource } from './aws-eks-addon';
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 @@ export default api;
api.set(ArtifactoryDatasource.id, new ArtifactoryDatasource());
api.set(AwsMachineImageDataSource.id, new AwsMachineImageDataSource());
api.set(AwsRdsDataSource.id, new AwsRdsDataSource());
api.set(AwsEKSAddonDataSource.id, new AwsEKSAddonDataSource());
api.set(AzureBicepResourceDatasource.id, new AzureBicepResourceDatasource());
api.set(AzurePipelinesTasksDatasource.id, new AzurePipelinesTasksDatasource());
api.set(BazelDatasource.id, new BazelDatasource());
Expand Down
111 changes: 111 additions & 0 deletions lib/modules/datasource/aws-eks-addon/__snapshots__/index.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`modules/datasource/aws-eks-addon/index getPkgReleases() with matched addon to return all versions of the addon 1`] = `
[
DescribeAddonVersionsCommand {
"deserialize": [Function],
"input": {
"addonName": "vpc-cni",
"kubernetesVersion": "1.30",
"maxResults": 1,
},
"middlewareStack": {
"add": [Function],
"addRelativeTo": [Function],
"applyToStack": [Function],
"clone": [Function],
"concat": [Function],
"identify": [Function],
"identifyOnResolve": [Function],
"remove": [Function],
"removeByTag": [Function],
"resolve": [Function],
"use": [Function],
},
"serialize": [Function],
},
]
`;

exports[`modules/datasource/aws-eks-addon/index getPkgReleases() with matched addon to return all versions of the addon 2`] = `
{
"addons": [
{
"addonName": "vpc-cni",
"addonVersions": [
{
"addonVersion": "v1.18.1-eksbuild.1",
"architecture": [
"amd64",
"arm64",
],
"compatibilities": [
{
"clusterVersion": "1.30",
"defaultVersion": false,
"platformVersions": [
"*",
],
},
],
"requiresConfiguration": false,
},
{
"addonVersion": "v1.18.2-eksbuild.1",
"architecture": [
"amd64",
"arm64",
],
"compatibilities": [
{
"clusterVersion": "1.30",
"defaultVersion": false,
"platformVersions": [
"*",
],
},
],
"requiresConfiguration": false,
},
],
"owner": "aws",
"publisher": "eks",
"type": "networking",
},
],
"nextToken": "mars-east-1",
}
`;

exports[`modules/datasource/aws-eks-addon/index getPkgReleases() without returned addons to be null 1`] = `
[
DescribeAddonVersionsCommand {
"deserialize": [Function],
"input": {
"addonName": "non-existing-addon",
"kubernetesVersion": "1.30",
"maxResults": 1,
},
"middlewareStack": {
"add": [Function],
"addRelativeTo": [Function],
"applyToStack": [Function],
"clone": [Function],
"concat": [Function],
"identify": [Function],
"identifyOnResolve": [Function],
"remove": [Function],
"removeByTag": [Function],
"resolve": [Function],
"use": [Function],
},
"serialize": [Function],
},
]
`;

exports[`modules/datasource/aws-eks-addon/index getPkgReleases() without returned addons to be null 2`] = `
{
"addons": [],
}
`;
125 changes: 125 additions & 0 deletions lib/modules/datasource/aws-eks-addon/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import {
type AddonInfo,
type AddonVersionInfo,
DescribeAddonVersionsCommand,
DescribeAddonVersionsResponse,
EKSClient,
} from '@aws-sdk/client-eks';
import { mockClient } from 'aws-sdk-client-mock';
import { getPkgReleases } from '../index';
jzhn marked this conversation as resolved.
Show resolved Hide resolved
import { AwsEKSAddonDataSource } from '.';

const datasource = AwsEKSAddonDataSource.id;
const eksMock = mockClient(EKSClient);

const addonVersion1: AddonVersionInfo = {
jzhn marked this conversation as resolved.
Show resolved Hide resolved
addonVersion: 'v1.18.1-eksbuild.1',
architecture: ['amd64', 'arm64'],
compatibilities: [
{
clusterVersion: '1.30',
platformVersions: ['*'],
defaultVersion: false,
},
],
requiresConfiguration: false,
};

const addonVersion2: AddonVersionInfo = {
addonVersion: 'v1.18.2-eksbuild.1',
architecture: ['amd64', 'arm64'],
compatibilities: [
{
clusterVersion: '1.30',
platformVersions: ['*'],
defaultVersion: false,
},
],
requiresConfiguration: false,
};

/**
* Testdata for mock implementation of EKSClient
*/
const vpcCniAddonInfo: AddonInfo = {
addonName: 'vpc-cni',
type: 'networking',
addonVersions: [addonVersion1, addonVersion2],
publisher: 'eks',
owner: 'aws',
};

const mockAddon: DescribeAddonVersionsResponse = {
addons: [vpcCniAddonInfo],
};

const mockEmpty: DescribeAddonVersionsResponse = {
addons: [],
};

function mockDescribeAddonVersionsCommand(
result: DescribeAddonVersionsResponse,
): void {
eksMock.reset();
eksMock.on(DescribeAddonVersionsCommand).resolves(result);
}

function mockDescribeAddonVersionsCommandWithRegion(
result: DescribeAddonVersionsResponse,
): void {
eksMock.reset();
eksMock
.on(DescribeAddonVersionsCommand)
.callsFake(async (input, getClient) => {
const client = getClient();
const region = await client.config.region();
return {
...result,
// put the client region as nextToken
// so that when we assert on the snapshot, we also verify that region from packageName is
// passed to aws client.
nextToken: region,
};
});
}

describe('modules/datasource/aws-eks-addon/index', () => {
describe('getPkgReleases()', () => {
it('without returned addons to be null', async () => {
mockDescribeAddonVersionsCommand(mockEmpty);
const res = await getPkgReleases({
datasource,
packageName:
'{"kubernetesVersion":"1.30","addonName":"non-existing-addon"}',
});
expect(res).toBeNull();
expect(eksMock.calls()).toHaveLength(1);
expect(eksMock.call(0).args).toMatchSnapshot();
expect(await eksMock.call(0).returnValue).toMatchSnapshot();
jzhn marked this conversation as resolved.
Show resolved Hide resolved
});

it('with matched addon to return all versions of the addon', async () => {
mockDescribeAddonVersionsCommandWithRegion(mockAddon);
const res = await getPkgReleases({
datasource,
packageName:
'{"kubernetesVersion":"1.30","addonName":"vpc-cni","region":"mars-east-1"}',
});
expect(res).toEqual({
releases: [
{
isDeprecated: false,
version: addonVersion1.addonVersion,
},
{
isDeprecated: false,
version: addonVersion2.addonVersion,
},
],
});
expect(eksMock.calls()).toHaveLength(1);
expect(eksMock.calls()[0].args).toMatchSnapshot();
expect(await eksMock.call(0).returnValue).toMatchSnapshot();
});
});
});
64 changes: 64 additions & 0 deletions lib/modules/datasource/aws-eks-addon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
DescribeAddonVersionsCommand,
DescribeAddonVersionsCommandInput,
EKSClient,
} from '@aws-sdk/client-eks';
import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
import { cache } from '../../../util/cache/package/decorator';
import { Datasource } from '../datasource';
import type { GetReleasesConfig, ReleaseResult } from '../types';
import type { EKSAddonsFilter } from './types';

export class AwsEKSAddonDataSource extends Datasource {
static readonly id = 'aws-eks-addon';

override readonly caching = true;

constructor() {
super(AwsEKSAddonDataSource.id);
}

@cache({
namespace: `datasource-${AwsEKSAddonDataSource.id}`,
key: ({ packageName }: GetReleasesConfig) => `getReleases:${packageName}`,
viceice marked this conversation as resolved.
Show resolved Hide resolved
})
async getReleases({
packageName: serializedFilter,
}: GetReleasesConfig): Promise<ReleaseResult | null> {
const filter: EKSAddonsFilter = JSON.parse(serializedFilter);
jzhn marked this conversation as resolved.
Show resolved Hide resolved
const eksClient = this.getEKSClient(filter);
jzhn marked this conversation as resolved.
Show resolved Hide resolved

const cmd = new DescribeAddonVersionsCommand(
this.getDescribeAddonsRequest(filter),
);
const response = await eksClient.send(cmd);
const addons = response.addons ?? [];
return {
releases: addons
.flatMap((addon) => addon.addonVersions)
.map((versionInfo) => ({
version: versionInfo!.addonVersion!,
jzhn marked this conversation as resolved.
Show resolved Hide resolved
isDeprecated: false,
jzhn marked this conversation as resolved.
Show resolved Hide resolved
})),
};
}

private getDescribeAddonsRequest({
kubernetesVersion,
addonName,
}: EKSAddonsFilter): DescribeAddonVersionsCommandInput {
// this API is paginated, but we only ever care about a single addon at a time.
return {
kubernetesVersion,
addonName,
maxResults: 1,
};
}

private getEKSClient({ region, profile }: EKSAddonsFilter): EKSClient {
return new EKSClient({
region,
credentials: fromNodeProviderChain({ profile }),
});
}
}
Loading
Loading