bazaarvoice / cloudformation-ruby-dsl

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

bringing in template snippets #84

Closed grimm26 closed 8 years ago

grimm26 commented 8 years ago

I have a cloudwatch alarm taht I would like to add any time I create an EC2 instance. The snippet looks like so:

    resource instance_name + 'RecoveryTestAlarm',
      Type: 'AWS::CloudWatch::Alarm',
      Properties: {
        AlarmDescription: 'Trigger a recovery when instance status check fails for 15 consecutive minutes.',
        Namespace: 'AWS/EC2' ,
        MetricName: 'StatusCheckFailed_System',
        Statistic: 'Minimum',
        Period: '60',
        EvaluationPeriods: '15',
        ComparisonOperator: 'GreaterThanThreshold',
        Threshold: '0',
        AlarmActions: [ join(':', "arn:aws:automate", ref('AWS::Region'), 'ec2:recover') ],
        Dimensions: [{Name: 'InstanceId',Value: ref(instance_name)}]
      }

Now, instance_name is the name of the AWS::EC2::Instance resource. Eventually I'll have more than this, so I'd like to abstract it into a file or a method. I have a class that I wrote to use with the dsl so I can pull it out of there. The problem with loading it from a file is the existence of the instance_name variable. load_from_file generates a complaint about not knowing what instance_name is. interpolate(file()) assumes I want to use it as UserData. Using a method to generate it seems to work but I'm not sure how to inject that into the template.

temujin9 commented 8 years ago

Method is the way to go, I think. You should be able to just call the method at the appropriate scope in the template for that resource.

grimm26 commented 8 years ago

The method will contain multiple complete resources, not just being used to fill in an attribute value (I do that all over the place). I'm not sure how to have the method inject entire resources into the template.

temujin9 commented 8 years ago

That shouldn't be unfeasible. All the resource declarations should happen in the same scope, so the same technique should work for multiple.

grimm26 commented 8 years ago

so, I created this method in my class:

     def recovery_test_alarm(instance_name)
        resource instance_name + 'RecoveryTestAlarm',
          Type: 'AWS::CloudWatch::Alarm',
          Properties: {
            AlarmDescription: 'Trigger a recovery when instance status check fails for 15 consecutive minutes.',
            Namespace: 'AWS/EC2' ,
            MetricName: 'StatusCheckFailed_System',
            Statistic: 'Minimum',
            Period: '60',
            EvaluationPeriods: '15',
            ComparisonOperator: 'GreaterThanThreshold',
            Threshold: '0',
            AlarmActions: [ join(':', "arn:#{arn_partition}:automate", ref('AWS::Region'), 'ec2:recover') ],
            Dimensions: [{Name: 'InstanceId',Value: ref(instance_name)}]
          }
      end

and when I try to use it in a template like myobject.recovery_test_alarm(name) this exception gets thrown:

in `recovery_test_alarm': undefined method `resource' for #<Myclass::Aws::CloudFormation:0x00000109507b48> (NoMethodError)```
jonaf commented 8 years ago

I'm not sure I would do it this way, but for that error to go away you need to import the ruby dsl into your class and then call it using TemplateDSL.resource. Since it's not a static method, you probably will have to new up a TemplateDSL instance to get it going.

A better way might be to have your class take a TemplateDSL (or a resource) and operate on it, returning the result. In this way, you basically pass the template around to your various helper methods / classes and mutate it along the way. If you want to be more functional about it, you could copy the object instead of passing the reference around.

temujin9 commented 8 years ago

Several options for accomplishing this without core library changes were proposed, and there were no further questions from end-user. Closing this for now.