mirror of
https://github.com/serverless/serverless.git
synced 2026-01-25 15:07:39 +00:00
Solve for these cases. The bug was that DVs can render to DVs when a value is being obtained from them (i.e. they are being de-referenced in getValueFromDeep). This is particularly problematic in the case that the original DV being rendered has a deep reference into the rendered DV. In that case the DV returned by rendering must be replaced by yet another DV.
While accomplishing the above, various bits of cleanup were implemented and I added some commentary around getDeeperValue for future engineers.
This case is particularly relevant if you have:
`serverless.yml`:
```
service: serverless-hello-world
provider:
name: aws
runtime: nodejs6.10
stage: dev
environment:
SECRET: ${self:custom.secrets.SECRET}
functions:
helloWorld:
handler: handler.helloWorld
events:
- http:
path: hello-world
method: get
cors: true
custom:
stage: ${opt:stage, self:provider.stage}
secrets: ${file(secrets.${self:custom.stage}.yml)}
```
AND
`secrets.dev.yml`:
```
SECRETS: secrets
```
Populating this service should result in the following sequence of rendering phases:
#########################
# PHASE 1
#
# ${self:custom.secrets.SECRET}
# <- ${deep:0.SECRET}
# deep[0] = ${file(secrets.${self:custom.stage}.yml)}
#
# ${opt:stage, self:provider.stage}
# <- 'dev'
#
# ${file(secrets.${self:custom.stage}.yml)}
# <- ${file(secrets.${deep:1}.yml)}
# deep[1] = ${opt:stage, self:provider.stage}
#
# RESULT
#
# service: serverless-hello-world
#
# provider:
# name: aws
# runtime: nodejs6.10
# stage: dev
# environment:
# SECRET: ${deep:0.SECRET}
#
# functions:
# helloWorld:
# handler: handler.helloWorld
# events:
# - http:
# path: hello-world
# method: get
# cors: true
#
# custom:
# stage: dev
# secrets: ${file(secrets.${deep:1}.yml)}
#########################
#########################
# PHASE 2
#
# ${deep:0.SECRET}
# <- this.populateValue('${file(secrets.${self:custom.stage}.yml)}') => '${file(secrets.${deep:1}.yml)}' => '${deep:2}' => '${deep:2.SECRET}'
# deep[2] = ${file(secrets.${deep:1}.yml)}
#
# ${file(secrets.${deep:1}.yml)}
# <- this.populateValue('${file(secrets.${deep:1}.yml)}') => '${file(secrets.dev.yml)}' => '${deep:3}'
# deep[3] = ${file(secrets.dev.yml)}
#
# RESULT
#
# service: serverless-hello-world
#
# provider:
# name: aws
# runtime: nodejs6.10
# stage: dev
# environment:
# SECRET: ${deep:2.SECRET}
#
# functions:
# helloWorld:
# handler: handler.helloWorld
# events:
# - http:
# path: hello-world
# method: get
# cors: true
#
# custom:
# stage: dev
# secrets: ${deep:3}
#########################
#########################
# PHASE 3
#
# ${deep:2.SECRET}
# <- this.populateValue('${file(secrets.${deep:1}.yml)}') => '${file(secrets.dev.yml)}' => '${deep:3}' => '${deep:3.SECRET}'
# deep[3] is already set to ${file(secrets.dev.yml)}
#
# ${deep:3}
# <- this.populateValue('${file(secrets.dev.yml)}') => { SECRET: 'secret' }
#
# RESULT
#
# service: serverless-hello-world
#
# provider:
# name: aws
# runtime: nodejs6.10
# stage: dev
# environment:
# SECRET: ${deep:3.SECRET}
#
# functions:
# helloWorld:
# handler: handler.helloWorld
# events:
# - http:
# path: hello-world
# method: get
# cors: true
#
# custom:
# stage: dev
# secrets:
# SECRET: secret
#########################
#########################
# PHASE 4
#
# ${deep:3}
# <- this.populateValue('${file(secrets.dev.yml)}') => this.getDeeperValue(['SECRET'], { SECRET: 'secret' }) => 'secret'
#
# RESULT
#
# service: serverless-hello-world
#
# provider:
# name: aws
# runtime: nodejs6.10
# stage: dev
# environment:
# SECRET: secret
#
# functions:
# helloWorld:
# handler: handler.helloWorld
# events:
# - http:
# path: hello-world
# method: get
# cors: true
#
# custom:
# stage: dev
# secrets:
# SECRET: secret
#########################