@@ -20,12 +20,14 @@ const utils = require('./utils');
20
20
* @param currentTemplate {Object} Currently deployed main stack template
21
21
* @param aliasStackTemplates {Array<Object>} Currently deployed and references aliases
22
22
*/
23
- function mergeAliases ( stackName , newTemplate , currentTemplate , aliasStackTemplates ) {
23
+ function mergeAliases ( stackName , newTemplate , currentTemplate , aliasStackTemplates , currentAliasStackTemplate , removedResources ) {
24
+
25
+ const allAliasTemplates = _ . concat ( aliasStackTemplates , currentAliasStackTemplate ) ;
24
26
25
27
// Get all referenced function logical resource ids
26
28
const aliasedFunctions =
27
29
_ . flatMap (
28
- aliasStackTemplates ,
30
+ allAliasTemplates ,
29
31
template => _ . compact ( _ . map (
30
32
template . Resources ,
31
33
( resource , name ) => {
@@ -55,36 +57,22 @@ function mergeAliases(stackName, newTemplate, currentTemplate, aliasStackTemplat
55
57
_ . forEach ( usedFunctionElements . Resources , resources => _ . defaults ( newTemplate . Resources , resources ) ) ;
56
58
_ . forEach ( usedFunctionElements . Outputs , outputs => _ . defaults ( newTemplate . Outputs , outputs ) ) ;
57
59
58
- }
60
+ // Set references to obsoleted resources in fct env to "REMOVED" in case
61
+ // the alias that is removed was the last deployment of the stage.
62
+ // This will change the function definition, but that does not matter
63
+ // as is is neither aliased nor versioned
64
+ _ . forEach ( _ . filter ( newTemplate . Resources , [ 'Type' , 'AWS::Lambda::Function' ] ) , func => {
65
+ const refs = utils . findReferences ( func , removedResources ) ;
66
+ _ . forEach ( refs , ref => _ . set ( func , ref , "REMOVED" ) ) ;
67
+ } ) ;
59
68
60
- const defaultAliasFlags = {
61
- hasRole : false
62
- } ;
69
+ }
63
70
64
71
module . exports = {
65
72
66
- aliasInit ( currentTemplate , aliasStackTemplates ) {
67
- const aliasStack = this . _serverless . service . provider . compiledCloudFormationAliasTemplate ;
68
-
69
- // Prepare flags
70
- aliasStack . Outputs . AliasFlags = {
71
- Description : 'Alias flags.' ,
72
- Value : _ . assign ( { } , defaultAliasFlags )
73
- } ;
74
-
75
- _ . forEach ( aliasStackTemplates , aliasTemplate => {
76
- const flags = _ . get ( aliasTemplate , 'Outputs.AliasFlags' , '{}' ) ;
77
- try {
78
- _ . set ( aliasTemplate , 'Outputs.AliasFlags.Value' , _ . defaults ( JSON . parse ( flags ) , defaultAliasFlags ) ) ;
79
- } catch ( e ) {
80
- // Not handled
81
- }
82
- } ) ;
83
-
84
- return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates ] ) ;
85
- } ,
73
+ aliasInit : require ( './stackops/init' ) ,
86
74
87
- aliasHandleFunctions ( currentTemplate , aliasStackTemplates ) {
75
+ aliasHandleFunctions ( currentTemplate , aliasStackTemplates , currentAliasStackTemplate ) {
88
76
89
77
this . options . verbose && this . _serverless . cli . log ( 'Processing functions' ) ;
90
78
@@ -155,16 +143,16 @@ module.exports = {
155
143
}
156
144
157
145
// Merge function aliases and versions
158
- mergeAliases ( stackName , stageStack , currentTemplate , aliasStackTemplates ) ;
146
+ mergeAliases ( stackName , stageStack , currentTemplate , aliasStackTemplates , currentAliasStackTemplate , this . removedResourceKeys ) ;
159
147
160
148
// FIXME: Resource handling
161
149
// mergeResources()
162
150
163
151
// Promote the parsed templates to the promise chain.
164
- return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates ] ) ;
152
+ return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates , currentAliasStackTemplate ] ) ;
165
153
} ,
166
154
167
- aliasHandleApiGateway ( currentTemplate , aliasStackTemplates ) {
155
+ aliasHandleApiGateway ( currentTemplate , aliasStackTemplates , currentAliasStackTemplate ) {
168
156
169
157
const stackName = this . _provider . naming . getStackName ( ) ;
170
158
const stageStack = this . _serverless . service . provider . compiledCloudFormationTemplate ;
@@ -303,105 +291,16 @@ module.exports = {
303
291
304
292
_ . forEach ( aliasResources , resource => _ . assign ( aliasStack . Resources , resource ) ) ;
305
293
306
- return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates ] ) ;
294
+ return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates , currentAliasStackTemplate ] ) ;
307
295
} ,
308
296
309
- aliasHandleUserResources ( currentTemplate , aliasStackTemplates ) {
310
-
311
- const stageStack = this . _serverless . service . provider . compiledCloudFormationTemplate ;
312
- const aliasStack = this . _serverless . service . provider . compiledCloudFormationAliasTemplate ;
313
- const userResources = _ . get ( this . _serverless . service , 'resources' , { Resources : { } , Outputs : { } } ) ;
314
-
315
- this . options . verbose && this . _serverless . cli . log ( 'Processing custom resources' ) ;
316
-
317
- // Retrieve all resources referenced from other aliases
318
- const aliasDependencies = _ . reduce ( aliasStackTemplates , ( result , template ) => {
319
- try {
320
- const resourceRefs = JSON . parse ( _ . get ( template , 'Outputs.AliasResources.Value' , "[]" ) ) ;
321
- const outputRefs = JSON . parse ( _ . get ( template , 'Outputs.AliasOutputs.Value' , "[]" ) ) ;
322
- const resources = _ . assign ( { } , _ . pick ( _ . get ( currentTemplate , 'Resources' ) , resourceRefs , { } ) ) ;
323
- const outputs = _ . assign ( { } , _ . pick ( _ . get ( currentTemplate , 'Outputs' ) , outputRefs , { } ) ) ;
324
-
325
- // Check if there are IAM policy references for the alias resources and integrate them into
326
- // the lambda policy.
327
-
328
- _ . assign ( result . Resources , resources ) ;
329
- _ . assign ( result . Outputs , outputs ) ;
330
- return result ;
331
- } catch ( e ) {
332
- return result ;
333
- }
334
- } , { Resources : { } , Outputs : { } } ) ;
335
-
336
- // Logical resource ids are unique per stage
337
- // Alias stacks reference the used resources through an Output reference
338
- // On deploy, the plugin checks if a resource is already deployed from a stack
339
- // and does a validation of the resource properties
340
- // All used resources are copied from the current template
341
-
342
- // Extract the user resources that are not overrides of existing Serverless resources
343
- const currentResources =
344
- _ . assign ( { } ,
345
- _ . omitBy ( _ . get ( userResources , 'Resources' , { } ) , ( value , name ) => _ . includes ( _ . keys ( stageStack . Resources ) , name ) ) ) ;
346
-
347
- const currentOutputs = _ . get ( userResources , 'Outputs' , { } ) ;
348
-
349
- // Add the alias resources as output to the alias stack
350
- aliasStack . Outputs . AliasResources = {
351
- Description : 'Custom resource references' ,
352
- Value : JSON . stringify ( _ . keys ( currentResources ) )
353
- } ;
354
-
355
- // Add the outputs as output to the alias stack
356
- aliasStack . Outputs . AliasOutputs = {
357
- Description : 'Custom output references' ,
358
- Value : JSON . stringify ( _ . keys ( currentOutputs ) )
359
- } ;
360
-
361
- // FIXME: Deployments to the master (stage) alias should be allowed to reconfigure
362
- // resources and outputs. Otherwise a "merge" of feature branches into a
363
- // release branch would not be possible as resources would be rendered
364
- // immutable otherwise.
365
-
366
- // Check if the resource is already used anywhere else with a different definition
367
- _ . forOwn ( currentResources , ( resource , name ) => {
368
- if ( _ . has ( aliasDependencies . Resources , name ) && ! _ . isMatch ( aliasDependencies . Resources [ name ] , resource ) ) {
369
-
370
- // If we deploy the master alias, allow reconfiguration of resources
371
- if ( this . _alias === this . _stage && resource . Type === aliasDependencies . Resources [ name ] . Type ) {
372
- this . _serverless . cli . log ( `Reconfigure resource ${ name } . Remember to update it in other aliases too.` ) ;
373
- } else {
374
- return BbPromise . reject ( new Error ( `Resource ${ name } is already deployed in another alias with a different configuration. Either you change your resource to match the other definition, or you change the logical resource id to deploy your resource separately.` ) ) ;
375
- }
376
- }
377
- } ) ;
378
-
379
- // Check if the output is already used anywhere else with a different definition
380
- _ . forOwn ( currentOutputs , ( output , name ) => {
381
- if ( _ . has ( aliasDependencies . Outputs , name ) && ! _ . isMatch ( aliasDependencies . Outputs [ name ] , output ) ) {
382
- if ( this . _alias === this . _stage ) {
383
- this . _serverless . cli . log ( `Reconfigure output ${ name } . Remember to update it in other aliases too.` ) ;
384
- } else {
385
- return BbPromise . reject ( new Error ( `Output ${ name } is already deployed in another alias with a different configuration. Either you change your output to match the other definition, or you change the logical resource id to deploy your output separately.` ) ) ;
386
- }
387
- }
388
- } ) ;
389
-
390
- // Merge used alias resources and outputs into the stage
391
- _ . defaults ( stageStack . Resources , aliasDependencies . Resources ) ;
392
- _ . defaults ( stageStack . Outputs , aliasDependencies . Outputs ) ;
393
-
394
- //console.log(JSON.stringify(aliasDependencies, null, 2));
395
- //throw new Error('iwzgeiug');
396
-
397
- return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates ] ) ;
398
- } ,
297
+ aliasHandleUserResources : require ( './stackops/userResources' ) ,
399
298
400
299
/**
401
300
* Merge alias and current stack policies, so that all alias policy statements
402
301
* are present and active
403
302
*/
404
- aliasHandleLambdaRole ( currentTemplate , aliasStackTemplates ) {
303
+ aliasHandleLambdaRole ( currentTemplate , aliasStackTemplates , currentAliasStackTemplate ) {
405
304
406
305
const stageStack = this . _serverless . service . provider . compiledCloudFormationTemplate ;
407
306
const aliasStack = this . _serverless . service . provider . compiledCloudFormationAliasTemplate ;
@@ -427,7 +326,7 @@ module.exports = {
427
326
428
327
aliasStack . Outputs . AliasFlags . Value . hasRole = true ;
429
328
430
- return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates ] ) ;
329
+ return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates , currentAliasStackTemplate ] ) ;
431
330
}
432
331
433
332
// For now we only merge the first policy document and exit if SLS changes this behavior.
@@ -453,6 +352,23 @@ module.exports = {
453
352
}
454
353
} ) ;
455
354
355
+ // Remove all resource references of removed resources
356
+ const voidResourceRefs = utils . findReferences ( stageRolePolicyStatements , this . removedResourceKeys ) ;
357
+ const voidResourcePtrs = _ . compact ( _ . map ( voidResourceRefs , ref => {
358
+ const ptrs = / \[ ( [ 0 - 9 ] + ) \] .R e s o u r c e \[ ( [ 0 - 9 ] + ) \] .* / . exec ( ref ) ;
359
+ if ( ptrs && ptrs . length === 3 ) {
360
+ return { s : ptrs [ 1 ] , r : ptrs [ 2 ] } ;
361
+ }
362
+ return null ;
363
+ } ) ) ;
364
+ _ . forEach ( voidResourcePtrs , ptr => {
365
+ const statement = stageRolePolicyStatements [ ptr . s ] ;
366
+ _ . pullAt ( statement . Resource , [ ptr . r ] ) ;
367
+ if ( _ . isEmpty ( statement . Resource ) ) {
368
+ _ . pullAt ( stageRolePolicyStatements , [ ptr . s ] ) ;
369
+ }
370
+ } ) ;
371
+
456
372
// Insert statement dependencies
457
373
const dependencies = _ . reject ( ( ( ) => {
458
374
const result = [ ] ;
@@ -477,10 +393,10 @@ module.exports = {
477
393
stageStack . Resources [ dependency ] = currentTemplate . Resources [ dependency ] ;
478
394
} ) ;
479
395
480
- return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates ] ) ;
396
+ return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates , currentAliasStackTemplate ] ) ;
481
397
} ,
482
398
483
- aliasHandleEvents ( currentTemplate , aliasStackTemplates ) {
399
+ aliasHandleEvents ( currentTemplate , aliasStackTemplates , currentAliasStackTemplate ) {
484
400
485
401
const stageStack = this . _serverless . service . provider . compiledCloudFormationTemplate ;
486
402
const aliasStack = this . _serverless . service . provider . compiledCloudFormationAliasTemplate ;
@@ -508,26 +424,26 @@ module.exports = {
508
424
_ . defaults ( aliasStack . Resources , subscriptions ) ;
509
425
510
426
// Forward inputs to the promise chain
511
- return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates ] ) ;
427
+ return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates , currentAliasStackTemplate ] ) ;
512
428
} ,
513
429
514
- aliasFinalize ( currentTemplate , aliasStackTemplates ) {
430
+ aliasFinalize ( currentTemplate , aliasStackTemplates , currentAliasStackTemplate ) {
515
431
const aliasStack = this . _serverless . service . provider . compiledCloudFormationAliasTemplate ;
516
432
517
433
aliasStack . Outputs . AliasFlags . Value = JSON . stringify ( aliasStack . Outputs . AliasFlags . Value ) ;
518
434
519
- return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates ] ) ;
435
+ return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates , currentAliasStackTemplate ] ) ;
520
436
} ,
521
437
522
- aliasRestructureStack ( currentTemplate , aliasStackTemplates ) {
438
+ aliasRestructureStack ( currentTemplate , aliasStackTemplates , currentAliasStackTemplate ) {
523
439
524
440
this . _serverless . cli . log ( 'Preparing aliase ...' ) ;
525
441
526
442
if ( _ . isEmpty ( aliasStackTemplates ) && this . _stage !== this . _alias ) {
527
443
throw new this . _serverless . classes . Error ( new Error ( 'You have to deploy the master alias at least once with "serverless deploy"' ) ) ;
528
444
}
529
445
530
- return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates ] ) . bind ( this )
446
+ return BbPromise . resolve ( [ currentTemplate , aliasStackTemplates , currentAliasStackTemplate ] ) . bind ( this )
531
447
. spread ( this . aliasInit )
532
448
. spread ( this . aliasHandleUserResources )
533
449
. spread ( this . aliasHandleLambdaRole )
0 commit comments