Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Variables and Lookups #194

Merged
merged 18 commits into from
Aug 29, 2016

Conversation

mwildehahn
Copy link
Contributor

These will deprecate PARAMETERS and LOCAL_PARAMETERS.

Advantages:

  • No need to use CF parameters
  • Leverage yaml lists and dicts
  • Blueprint subclasses can easily add/overwrite parameters

Fixes: #175

I added: remind101/stacker_blueprints@93db0d2 to stacker_blueprints as a POC for showing how we can leverage the new BLUEPRINT_PARAMETERS

These will deprecate PARAMETERS and LOCAL_PARAMETERS.

Advantages:
- No need to use CF parameters
- Leverage yaml lists and dicts
- Blueprint subclasses can easily add/overwrite parameters
"""Resolve the values of the blueprint parameters.

resolve_parameters will resolve the values of the
`BLUEPRINT_PARAMETERS` with values from the command lie, the env file,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

@mwildehahn
Copy link
Contributor Author

Now supports doing the following:

DATABASE_URL: postgres://${empiredb_user}:${empiredb_password}@${empireDB::DBCname}/empire

empireDB::DBCname will get resolved at build time, empiredb_user will get resolved when we parse the config

dict: parameters available to the template

"""
if self.resolved_parameters is None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make this do the parameter resolution when it's called if resolved_parameters is None?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't want get_parameters to take any arguments and was invoking resolve_parameters where we were before: https://github.com/remind101/stacker/pull/194/files#diff-92ed81f5017042692188db7b5961c619R203. We need stack.parameters, provider and context which was readily available there, i'm not sure if we can get all of those values when we init the blueprint to make this resolve when called.

@phobologic
Copy link
Member

👍 other than the small comments I made. Thanks!

provider = MagicMock()
context = MagicMock()
provider.get_output.return_value = "Test Output"
output = resolve_string("some-stack::Output, other-stack::Output",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should ditch this old syntax when using blueprint_parameters in preference for the new template syntax.

@phobologic
Copy link
Member

One thing I thought of - what if instead of ${stack::output} being the generic syntax, what if we combined this with the concept of translators? Like, what if the output lookup was a sort of translator, but with the new syntax? Something like ${output MyStack::StackOutput}? Seems like that'd be more flexible (maybe it'd replace translators entirely?) and it'd make things more explicit. I suppose, since the output lookup is such a common thing, we could keep generic ${MyStack::StackOutput} to default to the output lookup.

@mwildehahn
Copy link
Contributor Author

we talked about this a little on slack, but to repeat for the record:

my concern with this is just that it gets confusing when the translator gets resolved. right now translators get resolved after the config is parsed, whereas outputs need to be resolved at build time. i think it would get confusing to have some translators resolved pre-build and others during build.

@troyready
Copy link
Contributor

Curious how this change would impact the generated templates' CFN parameters? Sounds like there won't be any, so I'm wondering if we could still add them to templates (using troposphere.add_parameter) while still using this new functionality?

It may be an errand of folly, but I'd like to keep adding CFN parameters for templates that I might share in built json form.

@phobologic
Copy link
Member

@troyready The idea was to eventually remove stacker support for parameters & local parameters in favor of these. That wouldn't mean that you couldn't still add them directly via troposphere, it just wouldn't have all the helper logic around building them into the blueprints.

The reason for that is that using real CFN parameters in stacker has sort of proven to be pointless. In theory you use parameters to make the templates re-usable, but in stacker you never actually re-use the templates (at least in the use cases I've seen - different environments usually have different buckets/namespaces - we end up with copies of the same template in two different buckets).

Do you often share the generated templates with others?

@troyready
Copy link
Contributor

It's definitely not something used in a normal stacker workflow, but it is nice to be able to share the json templates at times. It's something that I've done with colleagues (e.g. "even though you don't have stacker setup, you can just grab the template from this stack and use it with regular CFN").

Don't get me wrong, I'm not trying to argue for some crazy workflow that depends on that. I just have some reservation about a change that significantly alters what stacker can output (maybe that does mean I depend on spacebar heating?).

@mwildehahn
Copy link
Contributor Author

@troyready 😆 re: https://xkcd.com/1172/

We're now calling these variables so it isn't out of the realm of possibility that we keep PARAMETERS around in some form, but I'd like to remove them and allow people to just use add_parameter in a blueprint if they really want it.

@mwildehahn
Copy link
Contributor Author

After talking with @phobologic about the initial implementation of blueprint parameters, we decided to go with a concept of "variables" that wouldn't be as easy to confuse with CF parameters. Variables are only related to how you can build templates within stacker.

This PR now introduces two new classes to stacker:

Variable and Lookup. A Variable is a python class that represents the variable passed within the stacker config, ie:

  # Create a simple app within empire named "acme-inc"
  - name: acmeApp
    class_path: stacker_blueprints.empire.app.App
    variables:
      ServiceToken: ${empireDaemon::CustomResourcesTopic}
      Name: "acme-inc"

ServiceToken and Name are Variables.

A Variable can have zero or many Lookups within its value that will be resolved at build time and will then be available within the Blueprint as a dictionary of <variable name>: <resolved value>.

A Lookup is defined as ${<lookup type> <lookup input>} within the stacker config. We default to the output lookup since this is the most common use case. ie ${empireDaemon::CustomResourcesTopic} could be more verbosely defined as ${output empireDaemon::CustomResourcesTopic}.

Custom lookups can be registered within the config: https://github.com/remind101/stacker_blueprints/pull/24/files#diff-8f84670404b5e0466057d169e260c840R58, https://github.com/remind101/stacker_blueprints/pull/24/files#diff-7cb05c06f16d1eea2c363ecf8e0d1463R3

They can also be nested: https://github.com/remind101/stacker_blueprints/pull/24/files#diff-8f84670404b5e0466057d169e260c840R207

When we parse the config, we register any custom lookups that have been defined. At build time, we resolve all of the variables, invoking the lookup handlers that have been defined. When the Blueprint renders its template, variables will have been translated into a dictionary with any lookups resolved.

In the near future, we'll collapse the concept of "translators" into lookups given that their execution at build time and access to context and provider makes them much more powerful.

@@ -98,13 +104,15 @@ class Blueprint(object):
template.

"""
def __init__(self, name, context, mappings=None):

def __init__(self, name, context, variables=None, mappings=None):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't used

raise UnresolvedVariable(self, variable)

if variable.value is None:
logger.debug("Got None value for parameter %s, not submitting "
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/parameter/variable

@mwildehahn mwildehahn changed the title Implement new BLUEPRINT_PARAMETERS Implement new Variables and Lookups Aug 26, 2016
@mwildehahn mwildehahn changed the title Implement new Variables and Lookups Implement Variables and Lookups Aug 26, 2016
env file, the config, and any lookups resolved.

Args:
variables (list): list of variables
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a list of a specific class, right? Can you use the :class: syntax to point at the actual class that you want a list of? Right now it's hard to tell what a list of variables means.

@phobologic
Copy link
Member

This is looking so awesome. A couple of nits, and a few questions, but I'm really excited for this.

@phobologic
Copy link
Member

Also, once you're feeling comfortable with this, can you add some documentation? Thanks!

@mwildehahn
Copy link
Contributor Author

going to add docs in a separate PR, merging this in now!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants