1 | const globby = require( "globby" );
|
2 | const fs = require( "fs" );
|
3 | const request = require( "request" );
|
4 | const path = require( "path" );
|
5 | const AWS = require( "aws-sdk" );
|
6 | const _ = require( "lodash" );
|
7 | const cmd = require( "node-cmd" );
|
8 |
|
9 | function addLambdaEnvironmentVariablesToProcess ( environmentVariables ) {
|
10 | if ( environmentVariables ) {
|
11 | Object.keys( environmentVariables ).forEach( ( key ) => {
|
12 | process.env[ key ] = environmentVariables[ key ];
|
13 | });
|
14 | }
|
15 | }
|
16 |
|
17 | function authenticate () {
|
18 |
|
19 | return new Promise( ( resolve, reject ) => {
|
20 |
|
21 | if ( process.env.COLLY__USE_BASTION ) {
|
22 |
|
23 | console.log( "Authenticating via bastion servers..." );
|
24 |
|
25 | authenticateAgainstBastionService()
|
26 | .then( resolve );
|
27 | return;
|
28 |
|
29 | }
|
30 |
|
31 | if ( process.env.AWS_PROFILE ) {
|
32 |
|
33 | console.log( "Authenticating via local AWS profile..." );
|
34 | resolve();
|
35 | return;
|
36 |
|
37 | }
|
38 |
|
39 | reject( "No login credentials supplied" );
|
40 |
|
41 | });
|
42 |
|
43 | }
|
44 |
|
45 | function authenticateAgainstBastionService() {
|
46 |
|
47 | return new Promise( ( resolve, reject ) => {
|
48 |
|
49 | const projectConfig = getProjectConfig();
|
50 | const certPath = path.resolve( projectConfig.bastionService.certPath );
|
51 | const requestOptions = {
|
52 | url: projectConfig.bastionService.endpoint,
|
53 | agentOptions: {
|
54 | cert: fs.readFileSync( certPath ),
|
55 | key: fs.readFileSync( certPath ),
|
56 | ca: fs.readFileSync( projectConfig.bastionService.cloudServicesRoot )
|
57 | }
|
58 | };
|
59 |
|
60 | request.get( requestOptions, function ( error, response, body ) {
|
61 |
|
62 | if ( error || ( response && response.statusCode !== 200 ) ) {
|
63 |
|
64 | const statusCode = ( response && response.statusCode ) ? response.statusCode : 'no code';
|
65 | const errorMessage = `Unable to authenticate to AWS using the wormhole (Code: ${statusCode})`;
|
66 |
|
67 | reject( errorMessage );
|
68 |
|
69 | } else {
|
70 |
|
71 | const credentials = JSON.parse(body);
|
72 |
|
73 | process.env.AWS_ACCESS_KEY_ID = credentials.accessKeyId;
|
74 | process.env.AWS_SECRET_ACCESS_KEY = credentials.secretAccessKey;
|
75 | process.env.AWS_SESSION_TOKEN = credentials.sessionToken;
|
76 |
|
77 | resolve();
|
78 |
|
79 | }
|
80 |
|
81 | });
|
82 |
|
83 | });
|
84 |
|
85 | }
|
86 |
|
87 | function everythingAfterTheLastDot( dotDelimitedString ) {
|
88 | return dotDelimitedString.split(".").pop();
|
89 | }
|
90 |
|
91 | function getLambdaName ( nameOverride ) {
|
92 |
|
93 | const appendedToName = anyEnvButLive();
|
94 |
|
95 | return ( nameOverride || getLambdaConfigFile().name ) + appendedToName;
|
96 |
|
97 | }
|
98 |
|
99 | function anyEnvButLive () {
|
100 | return ( process.env.ENV !== "live" ) ? process.env.ENV.toUpperCase() : "";
|
101 | }
|
102 |
|
103 | function chooseProjectFile( envFiles, env ) {
|
104 | if ( env.trim() === "" ) {
|
105 | env = "live";
|
106 | }
|
107 | if ( env.toLowerCase() in envFiles ) {
|
108 | return envFiles[ env.toLowerCase() ];
|
109 | } else {
|
110 | console.log( "no env value defined" );
|
111 | throw new Error("`--env` parameter value has no matching colly file");
|
112 | }
|
113 | }
|
114 |
|
115 | function getLambdaConfigFilePath () {
|
116 | return `${process.env.COLLY__PROJECT_DIR}/${process.env.COLLY__LAMBDA_NAME}/function.json`;
|
117 | }
|
118 |
|
119 | function getLambdaConfigFile () {
|
120 | return JSON.parse( fs.readFileSync( getLambdaConfigFilePath() ) );
|
121 | }
|
122 |
|
123 | function getLambdaHandlerName () {
|
124 | const config = getLambdaConfigFile();
|
125 | return everythingAfterTheLastDot( config.handler );
|
126 | }
|
127 |
|
128 | function listEnvFiles () {
|
129 |
|
130 | const path = `${process.env.COLLY__PROJECT_DIR}/colly*.json`;
|
131 | let opts = {
|
132 | "dot": true
|
133 | };
|
134 | if ( path.substring(0,1) === "/" ) {
|
135 | opts.cwd = "/";
|
136 | }
|
137 | const relativePaths = globby.sync( path );
|
138 | let envFiles = {};
|
139 | relativePaths.forEach( ( relativePath ) => {
|
140 |
|
141 | const fileName = relativePath.split( "/" ).pop();
|
142 | const fileNameParts = fileName.split(".");
|
143 | let envName;
|
144 | if ( fileName === "colly.json" ) {
|
145 | envName = "live";
|
146 | }
|
147 |
|
148 | if ( fileNameParts.length > 2 ) {
|
149 | envName = fileNameParts[ 1 ];
|
150 | }
|
151 |
|
152 | envFiles[ envName ] = fileName;
|
153 |
|
154 | });
|
155 | return envFiles;
|
156 | }
|
157 |
|
158 | function getProjectConfigFilePath () {
|
159 | return `${process.env.COLLY__PROJECT_DIR}/${ chooseProjectFile( listEnvFiles(), process.env.ENV ) }`;
|
160 | }
|
161 |
|
162 | function getProjectConfig() {
|
163 | return JSON.parse( fs.readFileSync( getProjectConfigFilePath() ) );
|
164 | }
|
165 |
|
166 | function getLambdaFilePath( altStartOfPath ) {
|
167 |
|
168 | const startOfPath = altStartOfPath || process.env.COLLY__PROJECT_DIR;
|
169 |
|
170 | const config = getLambdaConfigFile( process.env.COLLY__LAMBDA_NAME );
|
171 |
|
172 | const relativePathToLambdaFile = config.handler.split( "." ).slice( 0, -1 ).join( "." ) + ".js";
|
173 |
|
174 | return startOfPath + "/" + relativePathToLambdaFile;
|
175 |
|
176 | }
|
177 |
|
178 | function copyAllFilesToDistDir () {
|
179 | return new Promise( ( resolve, reject ) => {
|
180 | const webpack = require( "webpack" );
|
181 | const config = getLambdaConfigFile( process.env.COLLY__LAMBDA_NAME );
|
182 | const webpackOutput = getLambdaFilePath( "./dist" );
|
183 | webpack({
|
184 | "context": process.env.COLLY__PROJECT_DIR,
|
185 | "entry": getLambdaFilePath(),
|
186 | "output": {
|
187 | "filename": webpackOutput,
|
188 | "libraryTarget": "commonjs"
|
189 | },
|
190 | "target": "node",
|
191 | "externals": [ "aws-sdk" ]
|
192 | }, ( err, stats ) => {
|
193 | if ( err || stats.hasErrors() ) {
|
194 | reject( err );
|
195 | }
|
196 | console.log( stats.toString( {
|
197 | colors: true
|
198 | } ) );
|
199 | resolve( `./dist/${process.env.COLLY__LAMBDA_NAME}` );
|
200 | });
|
201 | });
|
202 | }
|
203 |
|
204 | function optionsCustomiser ( objVal, srcVal, propName ) {
|
205 | const validOptionNames = [
|
206 | "env",
|
207 | "name",
|
208 | "event",
|
209 | "local",
|
210 | "use_bastion",
|
211 | "aws_profile"
|
212 | ];
|
213 | console.log( propName );
|
214 | if ( validOptionNames.indexOf( propName ) > -1 ) {
|
215 | if ( srcVal ) {
|
216 | return srcVal;
|
217 | } else {
|
218 | return objVal;
|
219 | }
|
220 | } else {
|
221 |
|
222 | }
|
223 | }
|
224 |
|
225 | function formatConfigFile( configFile ) {
|
226 | const propertyNameMap = {
|
227 | "useBastion": "use_bastion",
|
228 | "awsProfile": "aws_profile"
|
229 | }
|
230 | let formattedConfigFile = {};
|
231 | Object.keys( configFile ).forEach( propName => {
|
232 | if ( propName in propertyNameMap ) {
|
233 | formattedConfigFile[ propertyNameMap[ propName ] ] = configFile[ propName ];
|
234 | }
|
235 | });
|
236 | return formattedConfigFile;
|
237 | }
|
238 |
|
239 | function filterOptions( options, ) {
|
240 | const validOptionNames = [
|
241 | "env",
|
242 | "name",
|
243 | "event",
|
244 | "local",
|
245 | "use_bastion",
|
246 | "aws_profile",
|
247 | "context",
|
248 | "search",
|
249 | "start_time",
|
250 | "tail"
|
251 | ];
|
252 | return _.pickBy( options, ( val, name ) => {
|
253 | return ( validOptionNames.indexOf( name ) > -1 );
|
254 | })
|
255 | }
|
256 |
|
257 | function setOptions ( cliOptions ) {
|
258 |
|
259 | if ( cliOptions.env ) {
|
260 | process.env.ENV = cliOptions.env;
|
261 | } else {
|
262 | process.env.ENV = "live";
|
263 | }
|
264 |
|
265 | if ( !process.env.COLLY__PROJECT_DIR ) {
|
266 | process.env.COLLY__PROJECT_DIR = process.cwd();
|
267 | }
|
268 |
|
269 | projectConfig = getProjectConfig();
|
270 |
|
271 | const options = filterOptions( _.defaults( formatConfigFile( projectConfig ), cliOptions ) );
|
272 |
|
273 | if ( options.name ) {
|
274 | process.env.COLLY__LAMBDA_NAME = options.name;
|
275 | }
|
276 |
|
277 | if ( options.event ) {
|
278 | process.env.COLLY__LAMBDA_EVENT_FILE = options.event;
|
279 | }
|
280 |
|
281 | if ( options.context ) {
|
282 | process.env.COLLY__LAMBDA_CONTEXT_FILE = options.context;
|
283 | }
|
284 |
|
285 | if ( typeof options.local === "boolean" ) {
|
286 | process.env.COLLY__RUN_LAMBDA_LOCAL = options.local;
|
287 | }
|
288 |
|
289 | if ( options.use_bastion ) {
|
290 | process.env.COLLY__USE_BASTION = options.use_bastion;
|
291 | }
|
292 |
|
293 | if ( options.aws_profile ) {
|
294 | process.env.AWS_PROFILE = options.aws_profile;
|
295 | }
|
296 |
|
297 | if ( !process.env.COLLY__PROJECT_DIR ) {
|
298 | process.env.COLLY__PROJECT_DIR = process.cwd();
|
299 | }
|
300 |
|
301 | if ( options.search ) {
|
302 | process.env.COLLY__SEARCH = options.search;
|
303 | }
|
304 |
|
305 | if ( options.start_time ) {
|
306 | process.env.COLLY__START_TIME = options.start_time;
|
307 | }
|
308 |
|
309 | if ( typeof options.tail === "boolean" ) {
|
310 | process.env.COLLY__TAIL = options.tail;
|
311 | }
|
312 | }
|
313 |
|
314 | function setAwsRegion () {
|
315 | const projectConfig = getProjectConfig();
|
316 | AWS.config.region = projectConfig.region;
|
317 | }
|
318 |
|
319 | function lastItemInArray ( item, arr ) {
|
320 | return _.last( arr ) === item;
|
321 | }
|
322 |
|
323 | function addValueToLambdaConfig ( DotDelimitedProperty, value ) {
|
324 |
|
325 | let config = getLambdaConfigFile( process.env.LAMBDA_NAME );
|
326 |
|
327 | const propertyChain = DotDelimitedProperty.split( "." );
|
328 |
|
329 | let referencedPropertyToEdit = config;
|
330 |
|
331 | propertyChain.forEach( ( property, i, a ) => {
|
332 |
|
333 | if ( !( property in referencedPropertyToEdit ) ) {
|
334 | referencedPropertyToEdit[ property ] = {};
|
335 | }
|
336 |
|
337 | if ( lastItemInArray( property, propertyChain ) ) {
|
338 | referencedPropertyToEdit[ property ] = value;
|
339 | } else {
|
340 | referencedPropertyToEdit = referencedPropertyToEdit[ property ];
|
341 | }
|
342 |
|
343 | });
|
344 |
|
345 | fs.writeFileSync( getLambdaConfigFilePath( process.env.LAMBDA_NAME ), JSON.stringify( config, null, " " ) );
|
346 |
|
347 | }
|
348 |
|
349 | function zipFile ( package ) {
|
350 |
|
351 | return new Promise( ( resolve, reject ) => {
|
352 |
|
353 | console.log( "Zipping the webpack output... " );
|
354 |
|
355 | cmd.get( `cd dist && zip -9 -r ${process.env.COLLY__LAMBDA_NAME}.zip ${process.env.COLLY__LAMBDA_NAME}`, ( err, stdout ) => {
|
356 |
|
357 | console.log( stdout );
|
358 |
|
359 | resolve( `${package}.zip` );
|
360 |
|
361 | });
|
362 |
|
363 | });
|
364 |
|
365 | }
|
366 |
|
367 | function addEnvVarToProjectConfig ( name, value ) {
|
368 |
|
369 | let projectConfig = getProjectConfig();
|
370 | if ( !( "environmentVariables" in projectConfig ) ) {
|
371 | projectConfig.environmentVariables = {};
|
372 | }
|
373 | projectConfig.environmentVariables[ name ] = value;
|
374 | fs.writeFileSync( getProjectConfigFilePath(), JSON.stringify( projectConfig, null, " " ) );
|
375 |
|
376 | }
|
377 |
|
378 | module.exports = {
|
379 | "addEnvVarToProjectConfig": addEnvVarToProjectConfig,
|
380 | "addLambdaEnvironmentVariablesToProcess": addLambdaEnvironmentVariablesToProcess,
|
381 | "addValueToLambdaConfig": addValueToLambdaConfig,
|
382 | "anyEnvButLive": anyEnvButLive,
|
383 | "authenticate": authenticate,
|
384 | "chooseProjectFile": chooseProjectFile,
|
385 | "copyAllFilesToDistDir": copyAllFilesToDistDir,
|
386 | "everythingAfterTheLastDot": everythingAfterTheLastDot,
|
387 | "formatConfigFile": formatConfigFile,
|
388 | "getLambdaConfigFile": getLambdaConfigFile,
|
389 | "getLambdaConfigFilePath": getLambdaConfigFilePath,
|
390 | "getLambdaFilePath": getLambdaFilePath,
|
391 | "getLambdaHandlerName": getLambdaHandlerName,
|
392 | "getLambdaName": getLambdaName,
|
393 | "getProjectConfig": getProjectConfig,
|
394 | "getProjectConfigFilePath": getProjectConfigFilePath,
|
395 | "listEnvFiles": listEnvFiles,
|
396 | "setAwsRegion": setAwsRegion,
|
397 | "setOptions": setOptions,
|
398 | "zipFile": zipFile
|
399 | } |
\ | No newline at end of file |