EndpointDeploy: continue work on custom authoriers WIP

This commit is contained in:
ac360 2016-03-10 18:20:30 -08:00
parent 1cc6c6f689
commit 9c6793e22c
3 changed files with 212 additions and 213 deletions

View File

@ -19,13 +19,13 @@
module.exports = function(SPlugin, serverlessPath) {
const path = require('path'),
const path = require('path'),
SError = require(path.join(serverlessPath, 'Error')),
SCli = require(path.join(serverlessPath, 'utils/cli')),
BbPromise = require('bluebird'),
async = require('async'),
fs = require('fs'),
os = require('os');
let SUtils;
// Promisify fs module
@ -79,7 +79,7 @@ module.exports = function(SPlugin, serverlessPath) {
parameters: [
{
parameter: 'names',
description: 'The names/ids of the endpoints you want to deploy in this format: user/create#GET',
description: 'The names/ids of the endpoints you want to deploy in this format: user/create~GET',
position: '0->'
}
]
@ -160,8 +160,7 @@ module.exports = function(SPlugin, serverlessPath) {
_validateAndPrepare() {
let _this = this;
let _this = this;
_this.project = _this.S.getProject();
_this.aws = _this.S.getProvider();
_this.endpoints = [];
@ -171,7 +170,7 @@ module.exports = function(SPlugin, serverlessPath) {
// Prepare endpoints
if (_this.evt.options.names.length) {
_this.evt.options.names = _this.project.getEndpointsByName(_this.evt.options.names);
_this.endpoints = _this.project.getEndpointsByName(_this.evt.options.names);
}
// If CLI and no endpoint names targeted, deploy from CWD
@ -227,7 +226,5 @@ module.exports = function(SPlugin, serverlessPath) {
}
}
return( EndpointDeploy );
};

View File

@ -14,11 +14,12 @@
*/
module.exports = function(SPlugin, serverlessPath) {
const path = require('path'),
SError = require(path.join(serverlessPath, 'Error')),
SCli = require('../utils/cli'),
BbPromise = require('bluebird'),
fs = require('fs');
const path = require('path'),
SError = require(path.join(serverlessPath, 'Error')),
SCli = require('../utils/cli'),
BbPromise = require('bluebird'),
async = require('async'),
fs = require('fs');
let SUtils;
// Promisify fs module
@ -59,19 +60,19 @@ module.exports = function(SPlugin, serverlessPath) {
_this.endpoints = _this.project.getEndpointsByName(_this.evt.options.names);
return _this._validateAndPrepare()
.bind(_this)
.then(_this._processDeployment)
.then(_this._createDeployment)
.then(function() {
.bind(_this)
.then(_this._processDeployment)
.then(_this._createDeployment)
.then(function() {
/**
* Return EVT
*/
/**
* Return EVT
*/
_this.evt.data.deploymentId = _this.deployment.id;
return _this.evt;
_this.evt.data.deploymentId = _this.deployment.id;
return _this.evt;
});
});
}
/**
@ -96,19 +97,19 @@ module.exports = function(SPlugin, serverlessPath) {
_this.spinner.start();
return BbPromise.try(function() {
return _this.regions;
})
.bind(_this)
.each(function(region) {
return _this.regions;
})
.bind(_this)
.each(function(region) {
// Process each Region
return _this._processRegionDeployment(region);
})
.then(function() {
// Process each Region
return _this._processRegionDeployment(region);
})
.then(function() {
// Stop Spinner
_this.spinner.stop(true);
});
// Stop Spinner
_this.spinner.stop(true);
});
}
/**
@ -128,95 +129,96 @@ module.exports = function(SPlugin, serverlessPath) {
}
return _this._findOrCreateRestApi(
restApi,
_this.evt.options.stage,
region)
.then(function(restApiData) {
restApi,
_this.evt.options.stage,
region)
.then(function(restApiData) {
_this.restApiData = restApiData;
_this.restApiData = restApiData;
let regionInstance = _this.project.getRegion(_this.evt.options.stage, region);
regionInstance.addVariables({
apiGatewayApi: restApiData.name
});
let regionInstance = _this.project.getRegion(_this.evt.options.stage, region);
regionInstance.addVariables({
apiGatewayApi: restApiData.name
});
return regionInstance.save();
})
.then(_this._createAuthorizers)
.then(function() {
return new BbPromise(function (resolve, reject) {
return regionInstance.save();
})
.bind(_this)
.then(_this._createAuthorizers)
.then(function() {
return new BbPromise(function (resolve, reject) {
// A function can have multiple endpoints. Process all endpoints for this Function
async.eachSeries(_this.endpoints, function (endpoint, eCb) {
// A function can have multiple endpoints. Process all endpoints for this Function
async.eachSeries(_this.endpoints, function (endpoint, eCb) {
return BbPromise.try(function () {
return BbPromise.try(function () {
// Find authorizerId, if specified
let authorizerId;
if (endpoint.authorizerFunction) {
if (_this.authorizers[endpoint.authorizerFunction]) {
authorizerId = _this.authorizers[endpoint.authorizerFunction];
} else {
throw new SError(`Endpoint ${endpoint.getName()} has an 'authorizerFunction' specified that does not exist in this project.`);
}
// Find authorizerId, if specified
let authorizerId;
if (endpoint.authorizerFunction) {
if (_this.authorizers[endpoint.authorizerFunction]) {
authorizerId = _this.authorizers[endpoint.authorizerFunction];
} else {
throw new SError(`Endpoint ${endpoint.getName()} has an 'authorizerFunction' specified that does not exist in this project.`);
}
if (endpoint.authorizerId) {
authorizerId = endpoint.authorizerId;
}
if (endpoint.authorizerId) {
authorizerId = endpoint.authorizerId;
}
// Create new event object
let newEvt = {
options: {
stage: _this.evt.options.stage,
region: region,
name: endpoint.getName(),
authorizerId: authorizerId ? authorizerId : null,
aliasEndpoint: _this.evt.options.aliasEndpoint
}
};
// Create new event object
let newEvt = {
options: {
stage: _this.evt.options.stage,
region: region,
name: endpoint.getName(),
authorizerId: authorizerId ? authorizerId : null,
aliasEndpoint: _this.evt.options.aliasEndpoint
}
};
return _this.S.actions.endpointBuildApiGateway(newEvt);
})
.then(function (result) {
return _this.S.actions.endpointBuildApiGateway(newEvt);
})
.then(function (result) {
// Stash deployed endpoints
if (!_this.deployed) _this.deployed = {};
if (!_this.deployed[region]) _this.deployed[region] = [];
_this.deployed[region].push({
endpointPath: endpoint.path,
endpointMethod: endpoint.method,
endpointUrl: result.data.url
});
})
.catch(function (e) {
// Stash Failed Endpoint
if (!_this.failed) _this.failed = {};
if (!_this.failed[region]) _this.failed[region] = [];
_this.failed[region].push({
endpointPath: endpoint ? endpoint.path : 'unknown',
endpointMethod: endpoint ? endpoint.method : 'unknown',
message: e.message,
stack: e.stack
});
})
.then(function() {
// If no endpoints were successfully deployed, skip
if (!_this.deployed) return eCb();
// Deploy API Gateway Deployment in region
return _this._createDeployment()
.then(function() {
return eCb();
});
// Stash deployed endpoints
if (!_this.deployed) _this.deployed = {};
if (!_this.deployed[region]) _this.deployed[region] = [];
_this.deployed[region].push({
endpointPath: endpoint.path,
endpointMethod: endpoint.method,
endpointUrl: result.data.url
});
}, function () {
return resolve();
});
})
});
})
.catch(function (e) {
// Stash Failed Endpoint
if (!_this.failed) _this.failed = {};
if (!_this.failed[region]) _this.failed[region] = [];
_this.failed[region].push({
endpointPath: endpoint ? endpoint.path : 'unknown',
endpointMethod: endpoint ? endpoint.method : 'unknown',
message: e.message,
stack: e.stack
});
})
.then(function() {
// If no endpoints were successfully deployed, skip
if (!_this.deployed) return eCb();
// Deploy API Gateway Deployment in region
return _this._createDeployment()
.then(function() {
return eCb();
});
});
}, function () {
return resolve();
});
})
});
}
/**
@ -227,31 +229,31 @@ module.exports = function(SPlugin, serverlessPath) {
let _this = this;
return _this.provider.getApiByName(restApiName, stage, region)
.then(function(restApi) {
.then(function(restApi) {
// Return, if found
if (restApi) return restApi;
// Return, if found
if (restApi) return restApi;
// Otherwise, create new REST API
let params = {
name: restApiName, /* required */
description: 'A REST API for a Serverless project in region: ' + region
};
// Otherwise, create new REST API
let params = {
name: restApiName, /* required */
description: 'A REST API for a Serverless project in region: ' + region
};
return _this.provider.request('APIGateway', 'createRestApi', params, stage, region)
.then(function (response) {
return _this.provider.request('APIGateway', 'createRestApi', params, stage, region)
.then(function (response) {
SUtils.sDebug(
'"'
+ stage
+ ' - '
+ region
+ '": created a new REST API on AWS API Gateway with name: '
+ response.name);
SUtils.sDebug(
'"'
+ stage
+ ' - '
+ region
+ '": created a new REST API on AWS API Gateway with name: '
+ response.name);
return response;
});
});
return response;
});
});
}
/**
@ -266,77 +268,77 @@ module.exports = function(SPlugin, serverlessPath) {
return BbPromise.try(function() {
// Delete pre-existing on authorizers deployed on API Gateway
let params = {
restApiId: _this.evt.options.restApiId,
limit: 100
};
// Delete pre-existing on authorizers deployed on API Gateway
let params = {
restApiId: _this.restApiData.id,
limit: 100
};
return _this.provider.request('APIGateway', 'getAuthorizers', params, _this.evt.options.stage, _this.evt.options.region)
.then(function(a) {
return a.items;
});
})
.each(function(a) {
return _this.provider.request('APIGateway', 'getAuthorizers', params, _this.evt.options.stage, _this.evt.options.region)
.then(function(a) {
return a.items;
});
})
.each(function(a) {
// Otherwise, delete pre-existing on authorizers deployed on API Gateway
let params = {
restApiId: _this.evt.options.restApiId,
authorizerId: a.id
};
// Otherwise, delete pre-existing on authorizers deployed on API Gateway
let params = {
restApiId: _this.restApiData.id,
authorizerId: a.id
};
return _this.provider.request('APIGateway', 'deleteAuthorizer', params, _this.evt.options.stage, _this.evt.options.region);
})
.then(function() {
return functions;
})
.each(function(fn) {
return _this.provider.request('APIGateway', 'deleteAuthorizer', params, _this.evt.options.stage, _this.evt.options.region);
})
.then(function() {
return functions;
})
.each(function(fn) {
// If no authorizer data, skip
if (!fn.authorizer || !Object.keys(fn.authorizer).length) return;
// If no authorizer data, skip
if (!fn.authorizer || !Object.keys(fn.authorizer).length) return;
let f = fn.toObjectPopulated({ stage: _this.evt.options.stage, region: _this.evt.options.region });
let f = fn.toObjectPopulated({ stage: _this.evt.options.stage, region: _this.evt.options.region });
// Create new authorizer on API Gateway
// Create new authorizer on API Gateway
// Fetch Lambda
let params = {
FunctionName: fn.getDeployedName({ stage: _this.evt.options.stage, region: _this.evt.options.region }),
Qualifier: _this.evt.options.stage
};
// Fetch Lambda
let params = {
FunctionName: fn.getDeployedName({ stage: _this.evt.options.stage, region: _this.evt.options.region }),
Qualifier: _this.evt.options.stage
};
return _this.provider.request('Lambda', 'getFunction', params, _this.evt.options.stage, _this.evt.options.region)
.then(function(l) {
// Validate required authorizer params
if (!f.authorizer.identitySource) throw new SError(`Authorizer is missing identitySource property in function ${f.name}`);
return _this.provider.request('Lambda', 'getFunction', params, _this.evt.options.stage, _this.evt.options.region)
.then(function(l) {
// Validate required authorizer params
if (!f.authorizer.identitySource) throw new SError(`Authorizer is missing identitySource property in function ${f.name}`);
// Create Authorizer params. Set defaults.
let authorizer = f.authorizer;
authorizer.restApiId = _this.evt.options.restApiId;
authorizer.name = authorizer.name || fn.getDeployedName({ stage: _this.evt.options.stage, region: _this.evt.options.region });
authorizer.type = authorizer.type || 'TOKEN';
// Create Authorizer params. Set defaults.
let authorizer = f.authorizer;
authorizer.restApiId = _this.restApiData.id;
authorizer.name = authorizer.name || fn.getDeployedName({ stage: _this.evt.options.stage, region: _this.evt.options.region });
authorizer.type = authorizer.type || 'TOKEN';
let alias = '${stageVariables.functionAlias}';
let alias = '${stageVariables.functionAlias}';
// Construct authorizer URI
authorizer.authorizerUri = 'arn:aws:apigateway:'
+ _this.evt.options.region
+ ':lambda:path/2015-03-31/functions/arn:aws:lambda:'
+ _this.evt.options.region
+ ':'
+ _this.awsAccountNumber
+ ':function:'
+ fn.getDeployedName({ stage: _this.evt.options.stage, region: _this.evt.options.region })
+ ':'
+ alias
+ '/invocations';
// Construct authorizer URI
authorizer.authorizerUri = 'arn:aws:apigateway:'
+ _this.evt.options.region
+ ':lambda:path/2015-03-31/functions/arn:aws:lambda:'
+ _this.evt.options.region
+ ':'
+ _this.awsAccountNumber
+ ':function:'
+ fn.getDeployedName({ stage: _this.evt.options.stage, region: _this.evt.options.region })
+ ':'
+ alias
+ '/invocations';
return _this.provider.request('APIGateway', 'createAuthorizer', authorizer, _this.evt.options.stage, _this.evt.options.region)
.then(function(a) {
_this.authorizers[fn.name] = a.id;
});
});
});
return _this.provider.request('APIGateway', 'createAuthorizer', authorizer, _this.evt.options.stage, _this.evt.options.region)
.then(function(a) {
_this.authorizers[fn.name] = a.id;
});
});
});
}
/**
@ -352,7 +354,7 @@ module.exports = function(SPlugin, serverlessPath) {
let doDeploy = function() {
let params = {
restApiId: _this.evt.options.restApiId, /* required */
restApiId: _this.restApiData.id, /* required */
stageName: _this.evt.options.stage, /* required */
//cacheClusterEnabled: false, TODO: Implement
//cacheClusterSize: '0.5 | 1.6 | 6.1 | 13.5 | 28.4 | 58.2 | 118 | 237', TODO: Implement
@ -364,28 +366,28 @@ module.exports = function(SPlugin, serverlessPath) {
};
_this.provider.request('APIGateway', 'createDeployment', params, _this.evt.options.stage, _this.evt.options.region)
.then(function(response) {
.then(function(response) {
_this.deployment = response;
_this.deployment = response;
SUtils.sDebug(
'"'
+ _this.evt.options.stage
+ ' - '
+ _this.evt.options.region
+ ' - REST API: '
+ 'created API Gateway deployment: '
+ response.id);
SUtils.sDebug(
'"'
+ _this.evt.options.stage
+ ' - '
+ _this.evt.options.region
+ ' - REST API: '
+ 'created API Gateway deployment: '
+ response.id);
return resolve();
})
.catch(function(error) {
if( error.statusCode == 429 ) {
SUtils.sDebug("'Too many requests' received, sleeping 5 seconds");
setTimeout( doDeploy, 5000 );
} else
reject( new SError(error.message) );
});
return resolve();
})
.catch(function(error) {
if( error.statusCode == 429 ) {
SUtils.sDebug("'Too many requests' received, sleeping 5 seconds");
setTimeout( doDeploy, 5000 );
} else
reject( new SError(error.message) );
});
};
return doDeploy();

View File

@ -78,7 +78,7 @@ describe('Test Action: Endpoint Deploy', function() {
stage: config.stage,
region: config.region,
names: [
'group1/function1#GET'
'group1/function1~GET'
]
};
@ -106,7 +106,7 @@ describe('Test Action: Endpoint Deploy', function() {
stage: config.stage,
region: config.region,
names: [
'group2/function4#GET'
'group2/function4~GET'
]
};