bazaarvoice / cloudformation-ruby-dsl

Ruby DSL for creating Cloudformation templates
Apache License 2.0
210 stars 76 forks source link

Cannot create stack #86

Closed hjkatz closed 8 years ago

hjkatz commented 8 years ago

Ok, so this is a weird one.

I've been testing out this gem for a while and I think my company is going to make use of it. However I can't seem to create stacks.

When calling ./my_stack.rb create my_stack --parameters "blah" I receive the cryptic response of "Failed to create stack:" with no message. Looking into this with binding.pry I find that the exception being caught is on line 302 of lib/cloudformation-ruby-dsl/cfntemplate.rb. The exception class is Aws::CloudFormation::Errors::ValidationError which is not a class that is documented (I think it's autogenerated by the aws-sdk service layer). This exception does not have a message, code, or really any useful information.

Things that I have tried with varying results:

I have attached the ruby template I wrote, and I would love any and all help in fixing this issue.

Thanks! --Katz

Here's the exact commands I'm running:

Note: test.json is just ./database.cfn.rb expand > test.json

database.cfn.rb.txt

jonaf commented 8 years ago

Hi there,

  1. What version of the gem are you using?
  2. Were you able to obtain any kind of stack trace at all?
  3. You may be able to modify line 303 of cfntemplate.rb and try to get more information. I would try calling the code and context methods on the ServiceError (e). See: http://docs.aws.amazon.com/sdkforruby/api/Aws/Errors/ServiceError.html#code-instance_method

Something like this may work (untested):

    rescue Aws::CloudFormation::Errors::ServiceError => e
      ctx = e.context
      $stderr.puts "Failed to create stack (#{e.code}):"
      $stderr.puts "#{ctx.http_response.body_contents}"
      exit(false)
    end
hjkatz commented 8 years ago

Thanks for responding! I've attached my Gemfile.lock with all the version information. I have looked into the context variable and it's instance methods, but nothing seems useful in there either. For example, e.context.http_response.body_contents returns

"<ErrorResponse xmlns=\"http://cloudformation.amazonaws.com/doc/2010-05-15/\">\n  <Error>\n    <Type>Sender</Type>\n    <Code>ValidationError</Code>\n  </Error>\n  <RequestId>1985514e-1dcc-11e6-a76a-d322955e2e9a</RequestId>\n</ErrorResponse>\n"

Looking a bit harder and thinking into what I've changed from yesterday (working) to today (not working) I discovered that I may be using the built-in dsl tag function improperly. I'm attempting to take in a stack parameter for ParamCustomerLabel and I would like all resources to have that as a tag. However, I do not think AWS CloudFormation allows this, but the documentation is sparse. When I remove my 2 tag calls in the template, then the creation works as expected.

An immediate fix would be to just put back my duplicate tag code in every resource in the template. A better fix would be to add a new/different tag function that does this automatically for me at the resource level, not at the stack level.

I think I'm going to close this issue and look to make a pull request instead with these changes. Thanks for your help!

Gemfile.lock.txt

hjkatz commented 8 years ago

Just a quick extra note, perhaps adding some checks in the tag function for this kind of error (or adding documentation) would help others in the future.

jonaf commented 8 years ago

@hjkatz If you want to populate a tag with the value of a parameter, you can reference the parameter's static value at runtime using parameters['ParameterName'] rather than ref('ParameterName') which will generate a Cloudformation intrinsic attribute.

The formatting for tags should look something like this:

tag 'myTag', :Value => parameters['myParameter']

You can find more examples of ways you can write tags (and other DSL syntax) in examples/cloudformation-ruby-script.rb.

There is an open issue describing exactly what you've encountered: #75 . I agree that we definitely need to address this issue to surface tag-related errors more clearly.

hjkatz commented 8 years ago

Oh wonderful! That's super useful, but not currently in the example. Thanks though!

jonaf commented 8 years ago

It's easily (and frequently) overlooked, but the scope of the DSL itself is within the scope of the library, so you can reference pretty much anything at will. We happen to expose the parameters instance variable under a given name, but you can do the same thing with mappings using something like @dict.fetch(:Mappings)['myMapping'] for example. The reason it's not included in the documentation is because it was not exposed with the intention of being used this way originally; I've been thinking about expanding it in a standardized way so that it's easier to pull static values from the outer scope and encourage using the static values at runtime over the intrinsics.