Skip to content

Commit 12626a2

Browse files
authored
Upgrade scale up/down lambdas to aws sdk v3 (#7061)
Upgrade to aws sdk v3 Main change is getting rid of the `promise` calls since I think they just directly return promises instead of requests This changes a lot of mocks in the testing so I'm not sure how good running just `yarn test` is Testing: Mangled scaleDown to only run `listInstances` and `listSSMParameters` In `terraform-aws-github-runner/modules/runners/lambdas/runners`: ``` yarn build; cd dist;node -e 'require("./index").scaleDown({}, {}, {});' > t.log ``` I also tried to terminate a runner and it worked Deployed to pytorch-canary and ran some jobs, seems ok
1 parent a98113d commit 12626a2

21 files changed

+2366
-2359
lines changed

terraform-aws-github-runner/modules/runners/lambdas/runners/jest.config.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,4 @@ module.exports = {
1212
statements: 94
1313
}
1414
},
15-
moduleNameMapper: {
16-
axios: 'axios/dist/node/axios.cjs', // Allow axios to work in tests
17-
},
1815
};

terraform-aws-github-runner/modules/runners/lambdas/runners/package.json

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,26 @@
1818
"devDependencies": {
1919
"@types/aws-lambda": "^8.10.72",
2020
"@types/express": "^4.17.11",
21-
"@types/jest": "^26.0.20",
21+
"@types/jest": "29",
2222
"@typescript-eslint/eslint-plugin": "^5.30.5",
2323
"@typescript-eslint/parser": "^5.30.5",
2424
"@vercel/ncc": "^0.38.1",
2525
"eslint": "^8.19.0",
26-
"jest": "^26.6.3",
26+
"jest": "29",
2727
"jest-mock-extended": "^1.0.13",
2828
"moment-timezone": "^0.5.35",
2929
"nock": "^13.0.11",
3030
"prettier": "^2.4.1",
31-
"ts-jest": "^26.5.3",
31+
"ts-jest": "29",
3232
"ts-node-dev": "^1.1.6"
3333
},
3434
"dependencies": {
35+
"@aws-sdk/client-cloudwatch": "^3.876.0",
36+
"@aws-sdk/client-ec2": "^3.876.0",
37+
"@aws-sdk/client-kms": "^3.876.0",
38+
"@aws-sdk/client-secrets-manager": "^3.876.0",
39+
"@aws-sdk/client-sqs": "^3.876.0",
40+
"@aws-sdk/client-ssm": "^3.876.0",
3541
"@octokit/auth-app": "^3.0.0",
3642
"@octokit/request-error": "^2.1.0",
3743
"@octokit/rest": "^18.3.5",
@@ -41,7 +47,6 @@
4147
"@types/node": "^14.14.34",
4248
"@types/uuid": "^9.0.1",
4349
"async-mutex": "^0.4.0",
44-
"aws-sdk": "^2.863.0",
4550
"axios": "^1.8.2",
4651
"cron-parser": "^3.3.0",
4752
"generic-pool": "^3.9.0",

terraform-aws-github-runner/modules/runners/lambdas/runners/src/lambda.test.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,17 @@ import { scaleDown as scaleDownL, scaleUp as scaleUpL, scaleUpChron as scaleUpCh
33
import nock from 'nock';
44
import { Config } from './scale-runners/config';
55
import { Context, SQSEvent, ScheduledEvent } from 'aws-lambda';
6-
import { mocked } from 'ts-jest/utils';
6+
import { mocked } from 'jest-mock';
77
import { scaleDown } from './scale-runners/scale-down';
88
import { scaleUp, RetryableScalingError } from './scale-runners/scale-up';
99
import { scaleUpChron } from './scale-runners/scale-up-chron';
1010
import { sqsSendMessages, sqsDeleteMessageBatch } from './scale-runners/sqs';
1111
import * as MetricsModule from './scale-runners/metrics';
1212

1313
const mockCloudWatch = {
14-
putMetricData: jest.fn().mockImplementation(() => {
15-
return { promise: jest.fn().mockResolvedValue(true) };
16-
}),
14+
putMetricData: jest.fn().mockResolvedValue(true),
1715
};
18-
jest.mock('aws-sdk', () => ({
16+
jest.mock('@aws-sdk/client-cloudwatch', () => ({
1917
CloudWatch: jest.fn().mockImplementation(() => mockCloudWatch),
2018
}));
2119

terraform-aws-github-runner/modules/runners/lambdas/runners/src/scale-runners/cache.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
getExperimentValue,
88
getJoinedStressTestExperiment,
99
} from './cache';
10-
import { mocked } from 'ts-jest/utils';
10+
import { mocked } from 'jest-mock';
1111
import { v4 as uuidv4 } from 'uuid';
1212
import nock from 'nock';
1313
import { RedisClientType, createClient } from 'redis';
@@ -47,7 +47,7 @@ beforeEach(() => {
4747
jest.restoreAllMocks();
4848
nock.disableNetConnect();
4949

50-
mocked(createClient).mockImplementation(produceMockedRedis);
50+
(mocked(createClient) as any).mockImplementation(produceMockedRedis);
5151

5252
jest.spyOn(Config, 'Instance', 'get').mockImplementation(() => config as unknown as Config);
5353
});

terraform-aws-github-runner/modules/runners/lambdas/runners/src/scale-runners/gh-auth.test.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,12 @@ jest.mock('./cache', () => ({
2828
}),
2929
}));
3030

31-
const mockSMgetSecretValuePromise = jest.fn();
3231
const mockSMgetSecretValue = jest.fn();
33-
jest.mock('aws-sdk', () => ({
32+
33+
jest.mock('@aws-sdk/client-secrets-manager', () => ({
3434
SecretsManager: jest.fn().mockImplementation(() => ({
3535
getSecretValue: mockSMgetSecretValue,
3636
})),
37-
CloudWatch: jest.requireActual('aws-sdk').CloudWatch,
3837
}));
3938

4039
const metrics = new ScaleUpMetrics();
@@ -95,8 +94,7 @@ describe('Test createGithubAuth', () => {
9594
describe('tests where aws-sdk fails', () => {
9695
const message = 'Error message on exception';
9796
beforeEach(() => {
98-
mockSMgetSecretValuePromise.mockClear().mockRejectedValue(Error(message));
99-
mockSMgetSecretValue.mockClear().mockImplementation(() => ({ promise: mockSMgetSecretValuePromise }));
97+
mockSMgetSecretValue.mockClear().mockRejectedValue(Error(message));
10098

10199
jest.spyOn(Config, 'Instance', 'get').mockImplementation(
102100
() =>
@@ -115,11 +113,10 @@ describe('Test createGithubAuth', () => {
115113

116114
describe('tests where aws-sdk works as expected', () => {
117115
beforeEach(() => {
118-
mockSMgetSecretValuePromise
116+
mockSMgetSecretValue
119117
.mockClear()
120118
.mockResolvedValueOnce({ SecretString: undefined })
121119
.mockResolvedValueOnce({ SecretString: secretString });
122-
mockSMgetSecretValue.mockClear().mockImplementation(() => ({ promise: mockSMgetSecretValuePromise }));
123120
});
124121

125122
describe('github keys are not from environment, nor secretsManagerSecretsId is provided', () => {
@@ -286,12 +283,12 @@ describe('Test createGithubAuth', () => {
286283
const result2 = await createGithubAuth(installationId, authType, '', metrics);
287284

288285
expect(mockSMgetSecretValue).toBeCalledTimes(2);
289-
expect(mockSMgetSecretValue).toHaveBeenCalledWith({ SecretId: Config.Instance.secretsManagerSecretsId });
290-
expect(mockSMgetSecretValuePromise).toBeCalledTimes(2);
286+
expect(mockSMgetSecretValue).toHaveBeenCalledWith({
287+
SecretId: Config.Instance.secretsManagerSecretsId,
288+
});
291289

292290
expect(mockedDecrypt).toBeCalledWith('github_app_client_secret', config.kmsKeyId, config.environment, metrics);
293291
expect(mockedDecrypt).toBeCalledWith('github_app_key_base64', config.kmsKeyId, config.environment, metrics);
294-
expect(mockSMgetSecretValuePromise).toBeCalledTimes(2);
295292
expect(mockedCreatAppAuth).toBeCalledWith(authOptions);
296293
expect(mockedAuth).toBeCalledWith({ type: authType });
297294
expect(result1).toBe(token);

terraform-aws-github-runner/modules/runners/lambdas/runners/src/scale-runners/gh-auth.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Config } from './config';
22
import { Metrics } from './metrics';
33
import { Octokit } from '@octokit/rest';
44
import { OctokitOptions } from '@octokit/core/dist-types/types';
5-
import { SecretsManager } from 'aws-sdk';
5+
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
66
import {
77
AppAuthentication,
88
InstallationAccessTokenAuthentication,
@@ -39,7 +39,7 @@ async function getCredentialsFromSecretsManager(
3939
metrics.smGetSecretValueAWSCallSuccess,
4040
metrics.smGetSecretValueAWSCallFailure,
4141
() => {
42-
return secretsManager.getSecretValue({ SecretId: secretsManagerSecretsId }).promise();
42+
return secretsManager.getSecretValue({ SecretId: secretsManagerSecretsId });
4343
},
4444
);
4545
});

terraform-aws-github-runner/modules/runners/lambdas/runners/src/scale-runners/gh-issues.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { getRepoIssuesWithLabel, resetIssuesCache } from './gh-issues';
22

33
import { Octokit } from '@octokit/rest';
44
import { createGitHubClientForRunnerRepo } from './gh-runners';
5-
import { mocked } from 'ts-jest/utils';
5+
import { mocked } from 'jest-mock';
66
import nock from 'nock';
77
import { ScaleUpMetrics } from './metrics';
88
import { redisCached } from './cache';

terraform-aws-github-runner/modules/runners/lambdas/runners/src/scale-runners/gh-runners.test.ts

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,9 @@ import { ScaleUpMetrics } from './metrics';
2020

2121
import { Config } from './config';
2222
import { Octokit } from '@octokit/rest';
23-
import { mocked } from 'ts-jest/utils';
23+
import { mocked } from 'jest-mock';
2424
import { locallyCached, redisCached } from './cache';
2525

26-
const mockEC2 = {
27-
describeInstances: jest.fn(),
28-
runInstances: jest.fn(),
29-
terminateInstances: jest.fn().mockReturnValue({ promise: jest.fn() }),
30-
};
31-
const mockSSMdescribeParametersRet = jest.fn();
32-
const mockSSM = {
33-
deleteParameter: jest.fn().mockReturnValue({ promise: jest.fn() }),
34-
describeParameters: jest.fn().mockReturnValue({ promise: mockSSMdescribeParametersRet }),
35-
putParameter: jest.fn().mockReturnValue({ promise: jest.fn() }),
36-
};
37-
jest.mock('aws-sdk', () => ({
38-
EC2: jest.fn().mockImplementation(() => mockEC2),
39-
SSM: jest.fn().mockImplementation(() => mockSSM),
40-
CloudWatch: jest.requireActual('aws-sdk').CloudWatch,
41-
}));
42-
4326
jest.mock('./gh-auth');
4427
jest.mock('./cache', () => ({
4528
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */

terraform-aws-github-runner/modules/runners/lambdas/runners/src/scale-runners/kms/index.test.ts

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import AWS from 'aws-sdk';
21
import { Config } from '../config';
32
import { decrypt } from './index';
43
import { ScaleUpMetrics } from '../metrics';
@@ -11,22 +10,12 @@ const mockKmsPromise = {
1110
toString: jest.fn().mockReturnValue(decryptedStr),
1211
},
1312
};
14-
const mockKmsDecrypt = {
15-
promise: jest.fn().mockImplementation(async () => mockKmsPromise),
16-
};
1713
const mockKms = {
18-
decrypt: jest.fn().mockImplementation(() => mockKmsDecrypt),
14+
decrypt: jest.fn().mockResolvedValue(mockKmsPromise),
1915
};
2016

21-
jest.mock('aws-sdk', () => ({
22-
__esModule: true,
23-
default: {
24-
config: {
25-
update: jest.fn(),
26-
},
27-
},
17+
jest.mock('@aws-sdk/client-kms', () => ({
2818
KMS: jest.fn().mockImplementation(() => mockKms),
29-
CloudWatch: jest.requireActual('aws-sdk').CloudWatch,
3019
}));
3120

3221
beforeEach(() => {
@@ -37,7 +26,7 @@ beforeEach(() => {
3726
});
3827

3928
describe('decrypt', () => {
40-
describe('check AWS && KMS calls', () => {
29+
describe('check KMS calls', () => {
4130
it('simple calls decrypt', async () => {
4231
const encrypted = Buffer.from('some random buffer');
4332
const key = 'decrypt key';
@@ -53,17 +42,13 @@ describe('decrypt', () => {
5342
expect(await decrypt(encrypted.toString('base64'), key, environmentName, new ScaleUpMetrics())).toBe(
5443
decryptedStr,
5544
);
56-
expect(AWS.config.update).toBeCalledWith({
57-
region: awsRegion,
58-
});
5945
expect(mockKms.decrypt).toBeCalledWith({
6046
CiphertextBlob: encrypted,
6147
KeyId: key,
6248
EncryptionContext: {
6349
['Environment']: environmentName,
6450
},
6551
});
66-
expect(mockKmsDecrypt.promise).toBeCalled();
6752
expect(mockKmsPromise.Plaintext.toString).toBeCalled();
6853
});
6954
});

terraform-aws-github-runner/modules/runners/lambdas/runners/src/scale-runners/kms/index.ts

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import AWS from 'aws-sdk';
1+
import { KMS } from '@aws-sdk/client-kms';
22
import { Config } from '../config';
33
import { expBackOff } from '../utils';
4-
import { KMS } from 'aws-sdk';
54
import { Metrics } from '../metrics';
65

76
let kms: KMS | undefined = undefined;
@@ -14,27 +13,23 @@ export async function decrypt(
1413
): Promise<string | undefined> {
1514
/* istanbul ignore next */
1615
if (!kms) {
17-
AWS.config.update({
16+
kms = new KMS({
1817
region: Config.Instance.awsRegion,
1918
});
20-
21-
kms = new KMS();
2219
}
2320

2421
// this is so the linter understands that KMS is not undefined at this point :(
2522
const kmsD = kms;
2623

2724
const decripted = await expBackOff(() => {
2825
return metrics.trackRequest(metrics.kmsDecryptAWSCallSuccess, metrics.kmsDecryptAWSCallFailure, () => {
29-
return kmsD
30-
.decrypt({
31-
CiphertextBlob: Buffer.from(encrypted, 'base64'),
32-
KeyId: key,
33-
EncryptionContext: {
34-
['Environment']: environmentName,
35-
},
36-
})
37-
.promise();
26+
return kmsD.decrypt({
27+
CiphertextBlob: Buffer.from(encrypted, 'base64'),
28+
KeyId: key,
29+
EncryptionContext: {
30+
['Environment']: environmentName,
31+
},
32+
});
3833
});
3934
});
4035

0 commit comments

Comments
 (0)