mirror of
https://github.com/serverless/serverless.git
synced 2026-01-18 14:58:43 +00:00
239 lines
7.9 KiB
JavaScript
239 lines
7.9 KiB
JavaScript
'use strict';
|
|
|
|
const AWS = require('aws-sdk');
|
|
const BbPromise = require('bluebird');
|
|
const HttpsProxyAgent = require('https-proxy-agent');
|
|
const url = require('url');
|
|
|
|
const naming = require('../lib/naming.js');
|
|
|
|
const constants = {
|
|
providerName: 'aws',
|
|
};
|
|
|
|
const impl = {
|
|
/**
|
|
* Add credentials, if present, from the given credentials configuration
|
|
* @param credentials The credentials to add credentials configuration to
|
|
* @param config The credentials configuration
|
|
*/
|
|
addCredentials: (credentials, config) => {
|
|
if (credentials &&
|
|
config &&
|
|
config.accessKeyId &&
|
|
config.accessKeyId !== 'undefined' &&
|
|
config.secretAccessKey &&
|
|
config.secretAccessKey !== 'undefined') {
|
|
if (config.accessKeyId) {
|
|
credentials.accessKeyId = config.accessKeyId; // eslint-disable-line no-param-reassign
|
|
}
|
|
if (config.secretAccessKey) {
|
|
// eslint-disable-next-line no-param-reassign
|
|
credentials.secretAccessKey = config.secretAccessKey;
|
|
}
|
|
if (config.sessionToken) {
|
|
credentials.sessionToken = config.sessionToken; // eslint-disable-line no-param-reassign
|
|
} else if (credentials.sessionToken) {
|
|
delete credentials.sessionToken; // eslint-disable-line no-param-reassign
|
|
}
|
|
}
|
|
},
|
|
/**
|
|
* Add credentials, if present, from the environment
|
|
* @param credentials The credentials to add environment credentials to
|
|
* @param prefix The environment variable prefix to use in extracting credentials
|
|
*/
|
|
addEnvironmentCredentials: (credentials, prefix) => {
|
|
if (prefix) {
|
|
const environmentCredentials = new AWS.EnvironmentCredentials(prefix);
|
|
impl.addCredentials(credentials, environmentCredentials);
|
|
}
|
|
},
|
|
/**
|
|
* Add credentials from a profile, if the profile exists
|
|
* @param credentials The credentials to add profile credentials to
|
|
* @param prefix The prefix to the profile environment variable
|
|
*/
|
|
addProfileCredentials: (credentials, profile) => {
|
|
if (profile) {
|
|
const profileCredentials = new AWS.SharedIniFileCredentials({ profile });
|
|
if (Object.keys(profileCredentials).length) {
|
|
credentials.profile = profile; // eslint-disable-line no-param-reassign
|
|
}
|
|
impl.addCredentials(credentials, profileCredentials);
|
|
}
|
|
},
|
|
/**
|
|
* Add credentials, if present, from a profile that is specified within the environment
|
|
* @param credentials The prefix of the profile's declaration in the environment
|
|
* @param prefix The prefix for the environment variable
|
|
*/
|
|
addEnvironmentProfile: (credentials, prefix) => {
|
|
if (prefix) {
|
|
const profile = process.env[`${prefix}_PROFILE`];
|
|
impl.addProfileCredentials(credentials, profile);
|
|
}
|
|
},
|
|
};
|
|
|
|
class AwsProvider {
|
|
static getProviderName() {
|
|
return constants.providerName;
|
|
}
|
|
|
|
constructor(serverless, options) {
|
|
this.naming = { provider: this };
|
|
this.options = options;
|
|
this.provider = this; // only load plugin in an AWS service context
|
|
this.serverless = serverless;
|
|
this.sdk = AWS;
|
|
this.serverless.setProvider(constants.providerName, this);
|
|
|
|
Object.assign(this.naming, naming);
|
|
|
|
// Use HTTPS Proxy (Optional)
|
|
const proxy = process.env.proxy
|
|
|| process.env.HTTP_PROXY
|
|
|| process.env.http_proxy
|
|
|| process.env.HTTPS_PROXY
|
|
|| process.env.https_proxy;
|
|
|
|
if (proxy) {
|
|
const proxyOptions = url.parse(proxy);
|
|
proxyOptions.secureEndpoint = true;
|
|
AWS.config.httpOptions.agent = new HttpsProxyAgent(proxyOptions);
|
|
}
|
|
|
|
// Configure the AWS Client timeout (Optional). The default is 120000 (2 minutes)
|
|
const timeout = process.env.AWS_CLIENT_TIMEOUT || process.env.aws_client_timeout;
|
|
if (timeout) {
|
|
AWS.config.httpOptions.timeout = parseInt(timeout, 10);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deprecated method. Moved to naming.js
|
|
* Kept here for backward compatibility
|
|
*/
|
|
getStackName(stage) {
|
|
const warningMessage = [
|
|
'Deprecation Notice: provider.getStackName() method is deprecated.',
|
|
' Please reference the naming.js file instead: ',
|
|
' ex. naming.getStackName();',
|
|
].join('');
|
|
|
|
this.serverless.cli.log(warningMessage);
|
|
return `${this.serverless.service.service}-${stage}`;
|
|
}
|
|
|
|
request(service, method, params) {
|
|
const that = this;
|
|
const credentials = that.getCredentials();
|
|
const persistentRequest = (f) => new BbPromise((resolve, reject) => {
|
|
const doCall = () => {
|
|
f()
|
|
.then(resolve)
|
|
.catch((e) => {
|
|
if (e.statusCode === 429) {
|
|
that.serverless.cli.log("'Too many requests' received, sleeping 5 seconds");
|
|
setTimeout(doCall, 5000);
|
|
} else {
|
|
reject(e);
|
|
}
|
|
});
|
|
};
|
|
return doCall();
|
|
});
|
|
|
|
return persistentRequest(() => {
|
|
const awsService = new that.sdk[service](credentials);
|
|
const req = awsService[method](params);
|
|
|
|
// TODO: Add listeners, put Debug statments here...
|
|
// req.on('send', function (r) {console.log(r)});
|
|
|
|
return new BbPromise((resolve, reject) => {
|
|
req.send((errParam, data) => {
|
|
const err = errParam;
|
|
if (err) {
|
|
if (err.message === 'Missing credentials in config') {
|
|
const errorMessage = [
|
|
'AWS provider credentials not found.',
|
|
' You can find more info on how to set up provider',
|
|
' credentials in our docs here: https://git.io/vXsdd',
|
|
].join('');
|
|
err.message = errorMessage;
|
|
}
|
|
reject(new this.serverless.classes.Error(err.message, err.statusCode));
|
|
} else {
|
|
resolve(data);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Fetch credentials directly or using a profile from serverless yml configuration or from the
|
|
* well known environment variables
|
|
* @returns {{region: *}}
|
|
*/
|
|
getCredentials() {
|
|
const returnValue = { region: this.getRegion() };
|
|
const credentials = {};
|
|
const stageUpper = this.getStage() ? this.getStage().toUpperCase() : null;
|
|
|
|
// add specified credentials, overriding with more specific declarations
|
|
impl.addCredentials(credentials, this.serverless.service.provider.credentials); // config creds
|
|
impl.addProfileCredentials(credentials, this.serverless.service.provider.profile);
|
|
impl.addEnvironmentCredentials(credentials, 'AWS'); // creds for all stages
|
|
impl.addEnvironmentProfile(credentials, 'AWS');
|
|
impl.addEnvironmentCredentials(credentials, `AWS_${stageUpper}`); // stage specific creds
|
|
impl.addEnvironmentProfile(credentials, `AWS_${stageUpper}`);
|
|
|
|
if (Object.keys(credentials).length) {
|
|
returnValue.credentials = credentials;
|
|
}
|
|
return returnValue;
|
|
}
|
|
|
|
getRegion() {
|
|
let returnValue = 'us-east-1';
|
|
if (this.options && this.options.region) {
|
|
returnValue = this.options.region;
|
|
} else if (this.serverless.config.region) {
|
|
returnValue = this.serverless.config.region;
|
|
} else if (this.serverless.service.provider.region) {
|
|
returnValue = this.serverless.service.provider.region;
|
|
}
|
|
return returnValue;
|
|
}
|
|
|
|
getServerlessDeploymentBucketName() {
|
|
if (this.serverless.service.provider.deploymentBucket) {
|
|
return BbPromise.resolve(this.serverless.service.provider.deploymentBucket);
|
|
}
|
|
return this.request('CloudFormation',
|
|
'describeStackResource',
|
|
{
|
|
StackName: this.naming.getStackName(),
|
|
LogicalResourceId: this.naming.getDeploymentBucketLogicalId(),
|
|
}
|
|
).then((result) => result.StackResourceDetail.PhysicalResourceId);
|
|
}
|
|
|
|
getStage() {
|
|
let returnValue = 'dev';
|
|
if (this.options && this.options.stage) {
|
|
returnValue = this.options.stage;
|
|
} else if (this.serverless.config.stage) {
|
|
returnValue = this.serverless.config.stage;
|
|
} else if (this.serverless.service.provider.stage) {
|
|
returnValue = this.serverless.service.provider.stage;
|
|
}
|
|
return returnValue;
|
|
}
|
|
}
|
|
|
|
module.exports = AwsProvider;
|