refactor: Revert bluebird from lib/plugins/aws

This reverts commit b11171c70c8f5597e2644f7ea8ec82a17a9eee29.
This commit is contained in:
Piotr Grzesik 2021-03-04 10:25:25 +01:00
parent 217b9751ea
commit 55abaaf6d5
11 changed files with 255 additions and 227 deletions

View File

@ -1,5 +1,6 @@
'use strict';
const BbPromise = require('bluebird');
const chalk = require('chalk');
const _ = require('lodash');
const dayjs = require('dayjs');
@ -87,7 +88,7 @@ class AwsMetrics {
const getMetrics = (params) =>
this.provider.request('CloudWatch', 'getMetricStatistics', params);
return Promise.all([
return BbPromise.all([
getMetrics(invocationsParams),
getMetrics(throttlesParams),
getMetrics(errorsParams),

View File

@ -1,6 +1,7 @@
'use strict';
const AWS = require('aws-sdk');
const BbPromise = require('bluebird');
const HttpsProxyAgent = require('https-proxy-agent');
const url = require('url');
const chalk = require('chalk');
@ -59,6 +60,8 @@ const apiGatewayUsagePlan = {
additionalProperties: false,
};
PromiseQueue.configure(BbPromise.Promise);
const MAX_RETRIES = (() => {
const userValue = Number(process.env.SLS_AWS_REQUEST_MAX_RETRIES);
return userValue >= 0 ? userValue : 4;
@ -1377,38 +1380,38 @@ class AwsProvider {
const paramsHash = objectHash.sha1(paramsWithRegion);
const BASE_BACKOFF = 5000;
const persistentRequest = (f) =>
new Promise((resolve, reject) => {
const doCall = async (numTry) => {
try {
resolve(await f());
} catch (e) {
const { providerError } = e;
if (
numTry < MAX_RETRIES &&
providerError &&
((providerError.retryable &&
providerError.statusCode !== 403 &&
providerError.code !== 'CredentialsError') ||
providerError.statusCode === 429)
) {
const nextTryNum = numTry + 1;
const jitter = Math.random() * 3000 - 1000;
// backoff is between 4 and 7 seconds
const backOff = BASE_BACKOFF + jitter;
new BbPromise((resolve, reject) => {
const doCall = (numTry) => {
f()
// We're resembling if/else logic, therefore single `then` instead of `then`/`catch` pair
.then(resolve, (e) => {
const { providerError } = e;
if (
numTry < MAX_RETRIES &&
providerError &&
((providerError.retryable &&
providerError.statusCode !== 403 &&
providerError.code !== 'CredentialsError') ||
providerError.statusCode === 429)
) {
const nextTryNum = numTry + 1;
const jitter = Math.random() * 3000 - 1000;
// backoff is between 4 and 7 seconds
const backOff = BASE_BACKOFF + jitter;
this.serverless.cli.log(
[
`Recoverable error occurred (${e.message}), sleeping for ~${Math.round(
backOff / 1000
)} seconds.`,
`Try ${nextTryNum} of ${MAX_RETRIES}`,
].join(' ')
);
setTimeout(doCall, backOff, nextTryNum);
} else {
reject(e);
}
}
this.serverless.cli.log(
[
`Recoverable error occurred (${e.message}), sleeping for ~${Math.round(
backOff / 1000
)} seconds.`,
`Try ${nextTryNum} of ${MAX_RETRIES}`,
].join(' ')
);
setTimeout(doCall, backOff, nextTryNum);
} else {
reject(e);
}
});
};
return doCall(0);
});
@ -1427,12 +1430,12 @@ class AwsProvider {
if (shouldCache) {
const cachedRequest = _.get(this.requestCache, `${service}.${method}.${paramsHash}`);
if (cachedRequest) {
return cachedRequest;
return BbPromise.resolve(cachedRequest);
}
}
const request = this.requestQueue.add(() => {
const result = persistentRequest(async () => {
const request = this.requestQueue.add(() =>
persistentRequest(() => {
if (options && options.region) {
credentials.region = options.region;
}
@ -1445,19 +1448,10 @@ class AwsProvider {
const promise = req.promise
? req.promise()
: new Promise((resolve, reject) => {
req.send((err, ...args) => {
if (err) {
reject(err);
return;
}
resolve(...args);
});
: BbPromise.fromCallback((cb) => {
req.send(cb);
});
try {
return await promise;
} catch (err) {
return promise.catch((err) => {
let message = err.message != null ? err.message : String(err.code);
if (message.startsWith('Missing credentials in config')) {
// Credentials error
@ -1479,22 +1473,27 @@ class AwsProvider {
: bottomError.message;
message = errorMessage;
// We do not want to trigger the retry mechanism for credential errors
throw Object.assign(new ServerlessError(errorMessage), {
providerError: Object.assign({}, err, { retryable: false }),
});
return BbPromise.reject(
Object.assign(new ServerlessError(errorMessage), {
providerError: Object.assign({}, err, { retryable: false }),
})
);
}
throw Object.assign(new ServerlessError(message), {
providerError: err,
});
return BbPromise.reject(
Object.assign(new ServerlessError(message), {
providerError: err,
})
);
});
}).then((data) => {
const result = BbPromise.resolve(data);
if (shouldCache) {
_.set(this.requestCache, `${service}.${method}.${paramsHash}`, result);
}
});
if (shouldCache) {
_.set(this.requestCache, `${service}.${method}.${paramsHash}`, result);
}
return result;
});
return result;
})
);
if (shouldCache) {
_.set(this.requestCache, `${service}.${method}.${paramsHash}`, request);
@ -1641,13 +1640,12 @@ class AwsProvider {
async getServerlessDeploymentBucketName() {
if (this.serverless.service.provider.deploymentBucket) {
return this.serverless.service.provider.deploymentBucket;
return BbPromise.resolve(this.serverless.service.provider.deploymentBucket);
}
const result = await this.request('CloudFormation', 'describeStackResource', {
return this.request('CloudFormation', 'describeStackResource', {
StackName: this.naming.getStackName(),
LogicalResourceId: this.naming.getDeploymentBucketLogicalId(),
});
return result.StackResourceDetail.PhysicalResourceId;
}).then((result) => result.StackResourceDetail.PhysicalResourceId);
}
getDeploymentPrefix() {
@ -1757,17 +1755,18 @@ class AwsProvider {
return stageSourceValue.value || defaultStage;
}
async getAccountInfo() {
const result = await this.request('STS', 'getCallerIdentity', {});
const arn = result.Arn;
const accountId = result.Account;
const partition = arn.split(':')[1]; // ex: arn:aws:iam:acctId:user/xyz
return {
accountId,
partition,
arn: result.Arn,
userId: result.UserId,
};
getAccountInfo() {
return this.request('STS', 'getCallerIdentity', {}).then((result) => {
const arn = result.Arn;
const accountId = result.Account;
const partition = arn.split(':')[1]; // ex: arn:aws:iam:acctId:user/xyz
return {
accountId,
partition,
arn: result.Arn,
userId: result.UserId,
};
});
}
/**
@ -1842,7 +1841,7 @@ class AwsProvider {
return { Ref: this.naming.getWebsocketsApiLogicalId() };
}
async getStackResources(next, resourcesParam) {
getStackResources(next, resourcesParam) {
let resources = resourcesParam;
const params = {
StackName: this.naming.getStackName(),
@ -1850,12 +1849,13 @@ class AwsProvider {
if (!resources) resources = [];
if (next) params.NextToken = next;
const res = await this.request('CloudFormation', 'listStackResources', params);
const allResources = resources.concat(res.StackResourceSummaries);
if (!res.NextToken) {
return allResources;
}
return this.getStackResources(res.NextToken, allResources);
return this.request('CloudFormation', 'listStackResources', params).then((res) => {
const allResources = resources.concat(res.StackResourceSummaries);
if (!res.NextToken) {
return allResources;
}
return this.getStackResources(res.NextToken, allResources);
});
}
async dockerPushToEcr(remoteTag, options = {}) {

View File

@ -1,11 +1,11 @@
'use strict';
const BbPromise = require('bluebird');
const validate = require('./lib/validate');
const setBucketName = require('./lib/setBucketName');
const updateStack = require('./lib/updateStack');
const monitorStack = require('./lib/monitorStack');
const findAndGroupDeployments = require('./utils/findAndGroupDeployments');
const ServerlessError = require('../../serverless-error');
class AwsRollback {
constructor(serverless, options) {
@ -16,7 +16,7 @@ class AwsRollback {
Object.assign(this, validate, setBucketName, updateStack, monitorStack);
this.hooks = {
'before:rollback:initialize': async () => this.validate(),
'before:rollback:initialize': async () => BbPromise.bind(this).then(this.validate),
'rollback:rollback': async () => {
if (!this.options.timestamp) {
const command = this.serverless.pluginManager.spawn('deploy:list');
@ -26,13 +26,13 @@ class AwsRollback {
'Run `sls rollback -t YourTimeStampHere`',
].join('\n')
);
await command;
return;
return command;
}
await this.setBucketName();
await this.setStackToUpdate();
await this.updateStack();
return BbPromise.bind(this)
.then(this.setBucketName)
.then(this.setStackToUpdate)
.then(this.updateStack);
},
};
}
@ -44,42 +44,45 @@ class AwsRollback {
const deploymentPrefix = this.provider.getDeploymentPrefix();
const prefix = `${deploymentPrefix}/${serviceName}/${stage}`;
const response = await this.provider.request('S3', 'listObjectsV2', {
Bucket: this.bucketName,
Prefix: prefix,
});
return this.provider
.request('S3', 'listObjectsV2', {
Bucket: this.bucketName,
Prefix: prefix,
})
.then((response) => {
const deployments = findAndGroupDeployments(response, deploymentPrefix, serviceName, stage);
const deployments = findAndGroupDeployments(response, deploymentPrefix, serviceName, stage);
if (deployments.length === 0) {
const msg = "Couldn't find any existing deployments.";
const hint = 'Please verify that stage and region are correct.';
return BbPromise.reject(`${msg} ${hint}`);
}
if (deployments.length === 0) {
const msg = "Couldn't find any existing deployments.";
const hint = 'Please verify that stage and region are correct.';
throw new ServerlessError(`${msg} ${hint}`);
}
let date = new Date(this.options.timestamp);
let date = new Date(this.options.timestamp);
// The if below is added due issues#5664 - Check it for more details
if (date instanceof Date === false || isNaN(date.valueOf())) {
date = new Date(Number(this.options.timestamp));
}
// The if below is added due issues#5664 - Check it for more details
if (date instanceof Date === false || isNaN(date.valueOf())) {
date = new Date(Number(this.options.timestamp));
}
const dateString = `${date.getTime().toString()}-${date.toISOString()}`;
const exists = deployments.some((deployment) =>
deployment.some(
(item) =>
item.directory === dateString &&
item.file === this.provider.naming.getCompiledTemplateS3Suffix()
)
);
const dateString = `${date.getTime().toString()}-${date.toISOString()}`;
const exists = deployments.some((deployment) =>
deployment.some(
(item) =>
item.directory === dateString &&
item.file === this.provider.naming.getCompiledTemplateS3Suffix()
)
);
if (!exists) {
const msg = `Couldn't find a deployment for the timestamp: ${this.options.timestamp}.`;
const hint = 'Please verify that the timestamp, stage and region are correct.';
return BbPromise.reject(`${msg} ${hint}`);
}
if (!exists) {
const msg = `Couldn't find a deployment for the timestamp: ${this.options.timestamp}.`;
const hint = 'Please verify that the timestamp, stage and region are correct.';
throw new ServerlessError(`${msg} ${hint}`);
}
service.package.artifactDirectoryName = `${prefix}/${dateString}`;
service.package.artifactDirectoryName = `${prefix}/${dateString}`;
return BbPromise.resolve();
});
}
}

View File

@ -1,5 +1,6 @@
'use strict';
const BbPromise = require('bluebird');
const validate = require('./lib/validate');
const fetch = require('node-fetch');
@ -42,12 +43,12 @@ class AwsRollbackFunction {
};
this.hooks = {
'rollback:function:rollback': async () => {
await this.validate();
const func = await this.getFunctionToBeRestored();
const funcCode = await this.fetchFunctionCode(func);
await this.restoreFunction(funcCode);
},
'rollback:function:rollback': async () =>
BbPromise.bind(this)
.then(this.validate)
.then(this.getFunctionToBeRestored)
.then(this.fetchFunctionCode)
.then(this.restoreFunction),
};
}
@ -67,27 +68,27 @@ class AwsRollbackFunction {
Qualifier: funcVersion,
};
try {
return this.provider.request('Lambda', 'getFunction', params);
} catch (error) {
if (error.message.match(/not found/)) {
const errorMessage = [
`Function "${funcName}" with version "${funcVersion}" not found.`,
` Please check if you've deployed "${funcName}"`,
` and version "${funcVersion}" is available for this function.`,
' Please check the docs for more info.',
].join('');
throw new Error(errorMessage);
}
throw new Error(error.message);
}
return this.provider
.request('Lambda', 'getFunction', params)
.then((func) => func)
.catch((error) => {
if (error.message.match(/not found/)) {
const errorMessage = [
`Function "${funcName}" with version "${funcVersion}" not found.`,
` Please check if you've deployed "${funcName}"`,
` and version "${funcVersion}" is available for this function.`,
' Please check the docs for more info.',
].join('');
throw new Error(errorMessage);
}
throw new Error(error.message);
});
}
async fetchFunctionCode(func) {
const codeUrl = func.Code.Location;
const response = await fetch(codeUrl);
return response.buffer();
return fetch(codeUrl).then((response) => response.buffer());
}
async restoreFunction(zipBuffer) {
@ -102,8 +103,9 @@ class AwsRollbackFunction {
ZipFile: zipBuffer,
};
await this.provider.request('Lambda', 'updateFunctionCode', params);
this.serverless.cli.log(`Successfully rolled back function "${this.options.function}"`);
return this.provider.request('Lambda', 'updateFunctionCode', params).then(() => {
this.serverless.cli.log(`Successfully rolled back function "${this.options.function}"`);
});
}
}

View File

@ -1,10 +1,9 @@
'use strict';
const { join } = require('path');
const fs = require('fs');
const { constants, readFile, writeFile, mkdir } = require('fs');
const os = require('os');
const { readFile, writeFile, mkdir } = fs.promises;
const BbPromise = require('bluebird');
const homedir = os.homedir();
const awsConfigDirPath = join(homedir, '.aws');
@ -44,37 +43,52 @@ const parseFileProfiles = (content) => {
return profiles;
};
const writeCredentialsContent = async (content) => {
try {
await writeFile(
const writeCredentialsContent = (content) =>
new BbPromise((resolve, reject) =>
writeFile(
credentialsFilePath,
content,
!isWindows ? { mode: fs.constants.S_IRUSR | fs.constants.S_IWUSR } : null
);
} catch (writeFileError) {
if (writeFileError.code === 'ENOENT') {
await mkdir(awsConfigDirPath, !isWindows ? { mode: fs.constants.S_IRWXU } : null);
await writeCredentialsContent(content);
} else {
throw writeFileError;
}
}
};
!isWindows ? { mode: constants.S_IRUSR | constants.S_IWUSR } : null,
(writeFileError) => {
if (writeFileError) {
if (writeFileError.code === 'ENOENT') {
mkdir(
awsConfigDirPath,
!isWindows ? { mode: constants.S_IRWXU } : null,
(mkdirError) => {
if (mkdirError) reject(mkdirError);
else resolve(writeCredentialsContent(content));
}
);
} else {
reject(writeFileError);
}
} else {
resolve();
}
}
)
);
module.exports = {
async resolveFileProfiles() {
if (!credentialsFilePath) {
return new Map();
}
try {
const content = await readFile(credentialsFilePath, { encoding: 'utf8' });
return parseFileProfiles(content);
} catch (err) {
if (err.code === 'ENOENT') {
return new Map();
resolveFileProfiles() {
return new BbPromise((resolve, reject) => {
if (!credentialsFilePath) {
resolve(new Map());
return;
}
throw err;
}
readFile(credentialsFilePath, { encoding: 'utf8' }, (error, content) => {
if (error) {
if (error.code === 'ENOENT') {
resolve(new Map());
return;
}
reject(error);
return;
}
resolve(parseFileProfiles(content));
});
});
},
resolveEnvCredentials() {
@ -85,23 +99,26 @@ module.exports = {
};
},
async saveFileProfiles(profiles) {
if (!credentialsFilePath) throw new Error('Could not resolve path to user credentials file');
await writeCredentialsContent(
`${Array.from(profiles)
.map(([name, data]) => {
const lineTokens = [`[${name}]`];
if (data.sessionToken) lineTokens.push(`aws_session_token=${data.sessionToken}`);
else {
lineTokens.push(
`aws_access_key_id=${data.accessKeyId}`,
`aws_secret_access_key=${data.secretAccessKey}`
);
}
return `${lineTokens.join('\n')}\n`;
})
.join('\n')}`
);
saveFileProfiles(profiles) {
return new BbPromise((resolve) => {
if (!credentialsFilePath) throw new Error('Could not resolve path to user credentials file');
resolve(
writeCredentialsContent(
`${Array.from(profiles)
.map(([name, data]) => {
const lineTokens = [`[${name}]`];
if (data.sessionToken) lineTokens.push(`aws_session_token=${data.sessionToken}`);
else {
lineTokens.push(
`aws_access_key_id=${data.accessKeyId}`,
`aws_secret_access_key=${data.secretAccessKey}`
);
}
return `${lineTokens.join('\n')}\n`;
})
.join('\n')}`
)
);
});
},
};

View File

@ -2,7 +2,6 @@
const os = require('os');
const _ = require('lodash');
const BbPromise = require('bluebird');
const jc = require('json-cycle');
const yaml = require('js-yaml');
const ServerlessError = require('../serverless-error');

View File

@ -3,6 +3,7 @@
/* eslint-disable no-unused-expressions */
const _ = require('lodash');
const BbPromise = require('bluebird');
const chai = require('chai');
const proxyquire = require('proxyquire');
const sinon = require('sinon');
@ -296,7 +297,7 @@ describe('AwsProvider', () => {
getRestApis() {
return {
promise: async () => ({ called: true }),
promise: () => BbPromise.resolve({ called: true }),
};
}
}
@ -733,10 +734,10 @@ describe('AwsProvider', () => {
}
);
const requests = [];
requests.push(executeRequestWithRegion('us-east-1'));
requests.push(executeRequestWithRegion('ap-northeast-1'));
requests.push(BbPromise.try(() => executeRequestWithRegion('us-east-1')));
requests.push(BbPromise.try(() => executeRequestWithRegion('ap-northeast-1')));
return Promise.all(requests)
return BbPromise.all(requests)
.then((results) => {
expect(Object.keys(results).length).to.equal(2);
results.forEach((result) => {
@ -785,19 +786,19 @@ describe('AwsProvider', () => {
awsProvider.request('CloudFormation', 'describeStacks', {}, { useCache: true });
const requests = [];
for (let n = 0; n < numTests; n++) {
requests.push(executeRequest());
requests.push(BbPromise.try(() => executeRequest()));
}
return Promise.all(requests)
return BbPromise.all(requests)
.then((results) => {
expect(Object.keys(results).length).to.equal(numTests);
results.forEach((result) => {
expect(result).to.deep.equal(expectedResult);
});
return Promise.all([
return BbPromise.join(
expect(sendStub).to.have.been.calledOnce,
expect(requestSpy).to.have.callCount(numTests),
]);
expect(requestSpy).to.have.callCount(numTests)
);
})
.finally(() => {
requestSpy.restore();

View File

@ -125,7 +125,7 @@ describe('AwsRollback', () => {
assert.isNotOk(true, 'setStackToUpdate should not resolve');
})
.catch((errorMessage) => {
expect(errorMessage.message).to.include("Couldn't find any existing deployments");
expect(errorMessage).to.include("Couldn't find any existing deployments");
expect(listObjectsStub.calledOnce).to.be.equal(true);
expect(
listObjectsStub.calledWithExactly('S3', 'listObjectsV2', {
@ -160,7 +160,7 @@ describe('AwsRollback', () => {
assert.isNotOk(true, 'setStackToUpdate should not resolve');
})
.catch((errorMessage) => {
expect(errorMessage.message).to.include("Couldn't find a deployment for the timestamp");
expect(errorMessage).to.include("Couldn't find a deployment for the timestamp");
expect(listObjectsStub.calledOnce).to.be.equal(true);
expect(
listObjectsStub.calledWithExactly('S3', 'listObjectsV2', {

View File

@ -1,6 +1,7 @@
'use strict';
const expect = require('chai').expect;
const BbPromise = require('bluebird');
const os = require('os');
const path = require('path');
const { outputFile, lstat, remove: rmDir } = require('fs-extra');
@ -56,7 +57,7 @@ describe('#credentials', () => {
`aws_secret_access_key = ${profile2.secretAccessKey}`,
].join('\n')}\n`;
return new Promise((resolve, reject) => {
return new BbPromise((resolve, reject) => {
outputFile(credentialsFilePath, credentialsFileContent, (error) => {
if (error) reject(error);
else resolve();

View File

@ -1,23 +1,25 @@
'use strict';
const expect = require('chai').expect;
const BbPromise = require('bluebird');
const resolveCfImportValue = require('../../../../../../lib/plugins/aws/utils/resolveCfImportValue');
describe('#resolveCfImportValue', () => {
it('should return matching exported value if found', () => {
const provider = {
request: async () => ({
Exports: [
{
Name: 'anotherName',
Value: 'anotherValue',
},
{
Name: 'exportName',
Value: 'exportValue',
},
],
}),
request: () =>
BbPromise.resolve({
Exports: [
{
Name: 'anotherName',
Value: 'anotherValue',
},
{
Name: 'exportName',
Value: 'exportValue',
},
],
}),
};
return resolveCfImportValue(provider, 'exportName').then((result) => {
expect(result).to.equal('exportValue');

View File

@ -1,6 +1,7 @@
'use strict';
const expect = require('chai').expect;
const BbPromise = require('bluebird');
const resolveCfRefValue = require('../../../../../../lib/plugins/aws/utils/resolveCfRefValue');
describe('#resolveCfRefValue', () => {
@ -9,18 +10,19 @@ describe('#resolveCfRefValue', () => {
naming: {
getStackName: () => 'stack-name',
},
request: async () => ({
StackResourceSummaries: [
{
LogicalResourceId: 'myS3',
PhysicalResourceId: 'stack-name-s3-id',
},
{
LogicalResourceId: 'myDB',
PhysicalResourceId: 'stack-name-db-id',
},
],
}),
request: () =>
BbPromise.resolve({
StackResourceSummaries: [
{
LogicalResourceId: 'myS3',
PhysicalResourceId: 'stack-name-s3-id',
},
{
LogicalResourceId: 'myDB',
PhysicalResourceId: 'stack-name-db-id',
},
],
}),
};
return resolveCfRefValue(provider, 'myDB').then((result) => {
expect(result).to.equal('stack-name-db-id');