cloudtools / stacker

An AWS CloudFormation Stack orchestrator/manager.
http://stacker.readthedocs.io/en/stable/
BSD 2-Clause "Simplified" License
709 stars 167 forks source link

Nested lookups do not generate dependency #664

Open nielslaukens opened 6 years ago

nielslaukens commented 6 years ago

When using the ${output stack::output} lookup, Stacker automatically generates a dependency for the current stack on the referenced stack. This behaviour works as expected. However, this behaviour changes when using nested lookups.

Steps to reproduce:

Use this blueprint as s3.py:

from stacker.blueprints.base import Blueprint
from troposphere import s3, Output

class Buckets(Blueprint):
    """
    Create a bucket with the given name, and output that name again.
    """
    VARIABLES = {
        "Bucket": {
            "type": object,
            "description": "S3 buckets to create"
        }
    }

    def create_template(self):
        t = self.template
        variables = self.get_variables()

        t.add_resource(s3.Bucket(
            "Bucket",
            BucketName=variables["Bucket"]
        ))

        t.add_output(Output(
            "BucketName",
            Value=variables["Bucket"],
        ))

with this config:

stacks:
  - name: source
    class_path: s3.Buckets
    variables:
      Bucket: unique-bucket-name

  - name: dest
    class_path: s3.Buckets
    variables:
      # Bucket: ${output source::name}    # this works as expected
      Bucket: ${output source::${default whatever::name}}

Note that, when creating from scratch, the dependency is not created. Hence the dest stack is created in parallel with the source stack, but fails to resolve the lookup:

[2018-09-26T10:47:28] Couldn't resolve lookup in variable `Bucket`, lookup: ${output source::name}: (<class 'TypeError'>) 'NoneType' object is not subscriptable
Traceback (most recent call last):
  File "<omitted>/stacker/lookups/registry.py", line 76, in resolve_lookups
    provider=provider,
  File "<omitted>/stacker/lookups/handlers/output.py", line 29, in handler
    return stack.outputs[d.output_name]
TypeError: 'NoneType' object is not subscriptable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<omitted>/stacker/stacker/plan.py", line 93, in _run_once
    status = self.fn(self.stack, status=self.status)
  File "<omitted>/stacker/stacker/actions/build.py", line 321, in _launch_stack
    stack.resolve(self.context, self.provider)
  File "<omitted>/stacker/stacker/stack.py", line 200, in resolve
    resolve_variables(self.variables, context, provider)
  File "<omitted>/stacker/stacker/variables.py", line 80, in resolve_variables
    variable.resolve(context, provider)
  File "<omitted>/stacker/stacker/variables.py", line 145, in resolve
    resolved_lookups = resolve_lookups(self, context, provider)
  File "<omitted>/stacker/stacker/lookups/registry.py", line 79, in resolve_lookups
    raise FailedVariableLookup(variable.name, lookup, e)
stacker.exceptions.FailedVariableLookup: Couldn't resolve lookup in variable `Bucket`, lookup: ${output source::name}: (<class 'TypeError'>) 'NoneType' object is not subscriptable
[2018-09-26T10:47:28] dest: failed (Couldn't resolve lookup in variable `Bucket`, lookup: ${output source::name}: (<class 'TypeError'>) 'NoneType' object is not subscriptable)
[2018-09-26T10:47:28] source: submitted (creating new stack)