cfndsl 0.4.4 → 0.5.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +5 -13
  2. data/.rubocop.yml +23 -0
  3. data/Gemfile +4 -0
  4. data/Rakefile +19 -17
  5. data/bin/cfndsl +20 -20
  6. data/cfndsl.gemspec +16 -15
  7. data/lib/cfndsl.rb +62 -68
  8. data/lib/cfndsl/aws/cloud_formation_template.rb +16 -0
  9. data/lib/cfndsl/aws/types.rb +12 -0
  10. data/lib/cfndsl/{aws_types.yaml → aws/types.yaml} +0 -0
  11. data/lib/cfndsl/{Conditions.rb → conditions.rb} +5 -7
  12. data/lib/cfndsl/creation_policy.rb +21 -0
  13. data/lib/cfndsl/errors.rb +29 -0
  14. data/lib/cfndsl/generate_types.rb +154 -0
  15. data/lib/cfndsl/jsonable.rb +214 -0
  16. data/lib/cfndsl/mappings.rb +23 -0
  17. data/lib/cfndsl/metadata.rb +16 -0
  18. data/lib/cfndsl/module.rb +52 -51
  19. data/lib/cfndsl/names.rb +5 -5
  20. data/lib/cfndsl/orchestration_template.rb +173 -0
  21. data/lib/cfndsl/os/heat_template.rb +16 -0
  22. data/lib/cfndsl/os/types.rb +12 -0
  23. data/lib/cfndsl/{os_types.yaml → os/types.yaml} +11 -11
  24. data/lib/cfndsl/{Outputs.rb → outputs.rb} +3 -4
  25. data/lib/cfndsl/{Parameters.rb → parameters.rb} +12 -13
  26. data/lib/cfndsl/plurals.rb +34 -0
  27. data/lib/cfndsl/properties.rb +21 -0
  28. data/lib/cfndsl/rake_task.rb +9 -7
  29. data/lib/cfndsl/ref_check.rb +44 -0
  30. data/lib/cfndsl/{Resources.rb → resources.rb} +13 -15
  31. data/lib/cfndsl/types.rb +151 -0
  32. data/lib/cfndsl/update_policy.rb +25 -0
  33. data/lib/cfndsl/version.rb +1 -1
  34. data/sample/autoscale.rb +152 -158
  35. data/sample/autoscale2.rb +151 -155
  36. data/sample/circular.rb +30 -33
  37. data/sample/codedeploy.rb +35 -36
  38. data/sample/config_service.rb +120 -0
  39. data/sample/ecs.rb +39 -39
  40. data/sample/iam_policies.rb +82 -0
  41. data/sample/lambda.rb +20 -24
  42. data/sample/s3.rb +11 -11
  43. data/sample/t1.rb +7 -9
  44. data/sample/vpc_example.rb +50 -0
  45. data/sample/vpc_with_vpn_example.rb +97 -0
  46. data/spec/cfndsl_spec.rb +22 -11
  47. data/spec/fixtures/heattest.rb +13 -14
  48. data/spec/fixtures/test.rb +56 -53
  49. metadata +36 -30
  50. data/lib/cfndsl/CloudFormationTemplate.rb +0 -267
  51. data/lib/cfndsl/CreationPolicy.rb +0 -25
  52. data/lib/cfndsl/Errors.rb +0 -31
  53. data/lib/cfndsl/JSONable.rb +0 -235
  54. data/lib/cfndsl/Mappings.rb +0 -25
  55. data/lib/cfndsl/Metadata.rb +0 -22
  56. data/lib/cfndsl/Plurals.rb +0 -35
  57. data/lib/cfndsl/Properties.rb +0 -25
  58. data/lib/cfndsl/RefCheck.rb +0 -48
  59. data/lib/cfndsl/Types.rb +0 -309
  60. data/lib/cfndsl/UpdatePolicy.rb +0 -29
  61. data/sample/config-service.rb +0 -119
  62. data/sample/iam-policies.rb +0 -82
  63. data/sample/vpc-example.rb +0 -51
  64. data/sample/vpc-with-vpn-example.rb +0 -97
File without changes
@@ -1,13 +1,11 @@
1
- require 'cfndsl/JSONable'
1
+ require 'cfndsl/jsonable'
2
2
 
3
3
  module CfnDsl
4
+ # Handles condition objects
5
+ #
6
+ # Usage:
7
+ # Condition :ConditionName, FnEqual(Ref(:ParameterName), 'helloworld')
4
8
  class ConditionDefinition < JSONable
5
- #
6
- # Handles condition objects
7
- #
8
- # Usage:
9
- # Condition :ConditionName, FnEqual(Ref(:ParameterName), 'helloworld')
10
-
11
9
  def initialize(value)
12
10
  @value = value
13
11
  end
@@ -0,0 +1,21 @@
1
+ require 'cfndsl/jsonable'
2
+
3
+ module CfnDsl
4
+ # Handles creation policy objects for Resources
5
+ #
6
+ # Usage
7
+ # Resource("aaa") {
8
+ # CreationPolicy('ResourceSignal', { 'Count' => 1, 'Timeout' => 'PT10M' })
9
+ # }
10
+ class CreationPolicyDefinition < JSONable
11
+ attr_reader :value
12
+
13
+ def initialize(value)
14
+ @value = value
15
+ end
16
+
17
+ def to_json(*a)
18
+ @value.to_json(*a)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ module CfnDsl
2
+ # Keeps track of errors
3
+ module Errors
4
+ @errors = []
5
+
6
+ def self.error(err, idx = nil)
7
+ if idx.nil?
8
+ @errors.push(err + "\n" + caller.join("\n") + "\n")
9
+ else
10
+ m = caller[idx].match(/^.*?:\d+:/)
11
+ err_loc = m ? m[0] : caller[idx]
12
+
13
+ @errors.push(err_loc + ' ' + err + "\n")
14
+ end
15
+ end
16
+
17
+ def self.clear
18
+ @errors = []
19
+ end
20
+
21
+ def self.errors
22
+ @errors
23
+ end
24
+
25
+ def self.errors?
26
+ !@errors.empty?
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,154 @@
1
+ require 'yaml'
2
+ require 'cfndsl/jsonable'
3
+ require 'cfndsl/plurals'
4
+ require 'cfndsl/names'
5
+
6
+ module CfnDsl
7
+ # Type generation helper
8
+ module GenerateTypes
9
+ # declare classes for all of the types with named methods for setting the values
10
+ class Type < JSONable
11
+ end
12
+
13
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
14
+ def generate_types(filename)
15
+ types = YAML.load(File.open(filename))
16
+ const_set('Types_Internal', types)
17
+
18
+ validate_types(types)
19
+
20
+ classes = {}
21
+
22
+ # Go through and declare all of the types first
23
+ types['Types'].each_key do |typename|
24
+ if !const_defined?(typename)
25
+ klass = const_set(typename, Class.new(self))
26
+ classes[typename] = klass
27
+ else
28
+ classes[typename] = const_get(typename)
29
+ end
30
+ end
31
+
32
+ # Now go through them again and define attribute setter methods
33
+ classes.each_pair do |typename, type|
34
+ typeval = types['Types'][typename]
35
+ next unless typeval.respond_to?(:each_pair)
36
+ typeval.each_pair do |attr_name, attr_type|
37
+ if attr_type.is_a?(Array)
38
+ klass = const_get(attr_type[0])
39
+ variable = "@#{attr_name}".to_sym
40
+
41
+ method = CfnDsl::Plurals.singularize(attr_name)
42
+ methods = attr_name
43
+ all_methods = CfnDsl.method_names(method) + CfnDsl.method_names(methods)
44
+ type.class_eval do
45
+ all_methods.each do |method_name|
46
+ define_method(method_name) do |value = nil, *rest, &block|
47
+ existing = instance_variable_get(variable)
48
+ # For no-op invocations, get out now
49
+ return existing if value.nil? && rest.empty? && !block
50
+
51
+ # We are going to modify the value in some
52
+ # way, make sure that we have an array to mess
53
+ # with if we start with nothing
54
+ existing = instance_variable_set(variable, []) unless existing
55
+
56
+ # special case for just a block, no args
57
+ if value.nil? && rest.empty? && block
58
+ val = klass.new
59
+ existing.push val
60
+ value.instance_eval(&block(val))
61
+ return existing
62
+ end
63
+
64
+ # Glue all of our parameters together into
65
+ # a giant array - flattening one level deep, if needed
66
+ array_params = []
67
+ if value.is_a?(Array)
68
+ value.each { |x| array_params.push x }
69
+ else
70
+ array_params.push value
71
+ end
72
+ unless rest.empty?
73
+ rest.each do |v|
74
+ if v.is_a?(Array)
75
+ array_params += rest
76
+ else
77
+ array_params.push v
78
+ end
79
+ end
80
+ end
81
+
82
+ # Here, if we were given multiple arguments either
83
+ # as method [a,b,c], method(a,b,c), or even
84
+ # method( a, [b], c) we end up with
85
+ # array_params = [a,b,c]
86
+ #
87
+ # array_params will have at least one item
88
+ # unless the user did something like pass in
89
+ # a bunch of empty arrays.
90
+ if block
91
+ # TODO: Is this a bug? We don't do anything with the array conetns
92
+ array_params.each do |_array_params_value|
93
+ value = klass.new
94
+ existing.push value
95
+ value.instance_eval(&block(val)) if block
96
+ end
97
+ else
98
+ # List of parameters with no block -
99
+ # hope that the user knows what he is
100
+ # doing and stuff them into our existing
101
+ # array
102
+ # TODO: Is this a bug? We don't do anything with the array conetns
103
+ array_params.each do |_array_params_value|
104
+ existing.push value
105
+ end
106
+ end
107
+ return existing
108
+ end
109
+ end
110
+ end
111
+ else
112
+ klass = const_get(attr_type)
113
+ variable = "@#{attr_name}".to_sym
114
+
115
+ type.class_eval do
116
+ CfnDsl.method_names(attr_name) do |inner_method|
117
+ define_method(inner_method) do |value = nil, *_rest, &block|
118
+ value ||= klass.new
119
+ instance_variable_set(variable, value)
120
+ value.instance_eval(&block) if block
121
+ value
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
130
+
131
+ private
132
+
133
+ # Do a little sanity checking - all of the types referenced in Resources
134
+ # should be represented in Types
135
+ def validate_types(types)
136
+ types['Resources'].values.each do |resource|
137
+ resource.values.each do |thing|
138
+ thing.values.flatten.each do |type|
139
+ puts "unknown type #{type}" unless types['Types'].key?(type)
140
+ end
141
+ end
142
+ end
143
+
144
+ # All of the type values should also be references
145
+ types['Types'].values do |type|
146
+ next unless type.respond_to?(:values)
147
+
148
+ type.values.each do |tv|
149
+ puts "unknown type #{tv}" unless types['Types'].key?(tv)
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,214 @@
1
+ require 'cfndsl/errors'
2
+ require 'cfndsl/ref_check'
3
+
4
+ module CfnDsl
5
+ # These functions are available anywhere inside
6
+ # a block for a JSONable object.
7
+ module Functions
8
+ # Equivalent to the CloudFormation template built in function Ref
9
+ def Ref(value)
10
+ RefDefinition.new(value)
11
+ end
12
+
13
+ # Equivalent to the CloudFormation template built in function Fn::Base64
14
+ def FnBase64(value)
15
+ Fn.new('Base64', value)
16
+ end
17
+
18
+ # Equivalent to the CloudFormation template built in function Fn::FindInMap
19
+ def FnFindInMap(map, key, value)
20
+ Fn.new('FindInMap', [map, key, value])
21
+ end
22
+
23
+ # Equivalent to the CloudFormation template built in function Fn::GetAtt
24
+ def FnGetAtt(logical_resource, attribute)
25
+ Fn.new('GetAtt', [logical_resource, attribute])
26
+ end
27
+
28
+ # Equivalent to the CloudFormation template built in function Fn::GetAZs
29
+ def FnGetAZs(region)
30
+ Fn.new('GetAZs', region)
31
+ end
32
+
33
+ # Equivalent to the CloudFormation template built in function Fn::Join
34
+ def FnJoin(string, array)
35
+ Fn.new('Join', [string, array])
36
+ end
37
+
38
+ # Equivalent to the CloudFormation template built in function Fn::And
39
+ def FnAnd(array)
40
+ if !array || array.count < 2 || array.count > 10
41
+ raise 'The array passed to Fn::And must have at least 2 elements and no more than 10'
42
+ end
43
+ Fn.new('And', array)
44
+ end
45
+
46
+ # Equivalent to the Cloudformation template built in function Fn::Equals
47
+ def FnEquals(value1, value2)
48
+ Fn.new('Equals', [value1, value2])
49
+ end
50
+
51
+ # Equivalent to the Cloudformation template built in function Fn::If
52
+ def FnIf(condition_name, true_value, false_value)
53
+ Fn.new('If', [condition_name, true_value, false_value])
54
+ end
55
+
56
+ # Equivalent to the Cloudformation template built in function Fn::Not
57
+ def FnNot(value)
58
+ Fn.new('Not', value)
59
+ end
60
+
61
+ # Equivalent to the CloudFormation template built in function Fn::Or
62
+ def FnOr(array)
63
+ if !array || array.count < 2 || array.count > 10
64
+ raise 'The array passed to Fn::Or must have at least 2 elements and no more than 10'
65
+ end
66
+ Fn.new('Or', array)
67
+ end
68
+
69
+ # Equivalent to the CloudFormation template built in function Fn::Select
70
+ def FnSelect(index, array)
71
+ Fn.new('Select', [index, array])
72
+ end
73
+
74
+ # Usage
75
+ # FnFormat('This is a %0. It is 100%% %1', 'test', 'effective')
76
+ # or
77
+ # FnFormat('This is a %{test}. It is 100%% %{effective}',
78
+ # :test => 'test",
79
+ # :effective => 'effective')
80
+ #
81
+ # These will each generate a call to Fn::Join that when
82
+ # evaluated will produce the string "This is a test. It is 100%
83
+ # effective."
84
+ #
85
+ # Think of this as %0, %1, etc in the format string being replaced by the
86
+ # corresponding arguments given after the format string. '%%' is replaced
87
+ # by the '%' character.
88
+ #
89
+ # The actual Fn::Join call corresponding to the above FnFormat call would be
90
+ # {"Fn::Join": ["",["This is a ","test",". It is 100","%"," ","effective"]]}
91
+ #
92
+ # If no arguments are given, or if a hash is given and the format
93
+ # variable name does not exist in the hash, it is used as a Ref
94
+ # to an existing resource or parameter.
95
+ #
96
+ # TODO Can we simplyfy this somehow?
97
+ # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
98
+ def FnFormat(string, *arguments)
99
+ array = []
100
+
101
+ if arguments.empty? || (arguments.length == 1 && arguments[0].instance_of?(Hash))
102
+ hash = arguments[0] || {}
103
+ string.scan(/(.*?)(?:%(%|\{([\w:]+)\})|\z)/m) do |x, y, z|
104
+ array.push x if x && !x.empty?
105
+
106
+ next unless y
107
+
108
+ array.push(y == '%' ? '%' : (hash[z] || hash[z.to_sym] || Ref(z)))
109
+ end
110
+ else
111
+ string.scan(/(.*?)(?:%(%|\d+)|\z)/m) do |x, y|
112
+ array.push x if x && !x.empty?
113
+
114
+ next unless y
115
+
116
+ array.push(y == '%' ? '%' : arguments[y.to_i])
117
+ end
118
+ end
119
+ Fn.new('Join', ['', array])
120
+ end
121
+ # rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
122
+ end
123
+
124
+ # This is the base class for just about everything useful in the
125
+ # DSL. It knows how to turn DSL Objects into the corresponding
126
+ # json, and it lets you create new built in function objects
127
+ # from inside the context of a dsl object.
128
+ class JSONable
129
+ include Functions
130
+ extend Functions
131
+ include RefCheck
132
+
133
+ # Use instance variables to build a json object. Instance
134
+ # variables that begin with a single underscore are elided.
135
+ # Instance variables that begin with two underscores have one of
136
+ # them removed.
137
+ def to_json(*a)
138
+ hash = {}
139
+ instance_variables.each do |var|
140
+ name = var[1..-1]
141
+
142
+ if name =~ /^__/
143
+ # if a variable starts with double underscore, strip one off
144
+ name = name[1..-1]
145
+ elsif name =~ /^_/
146
+ # Hide variables that start with single underscore
147
+ name = nil
148
+ end
149
+
150
+ hash[name] = instance_variable_get(var) if name
151
+ end
152
+ hash.to_json(*a)
153
+ end
154
+
155
+ def ref_children
156
+ instance_variables.map { |var| instance_variable_get(var) }
157
+ end
158
+
159
+ def declare(&block)
160
+ instance_eval(&block) if block_given?
161
+ end
162
+
163
+ def method_missing(meth, *args, &_block)
164
+ error = "Undefined symbol: #{meth}"
165
+ error = "#{error}(" + args.inspect[1..-2] + ')' unless args.empty?
166
+ error = "#{error}\n\nTry '#{titleize(meth)}' instead" if incorrect_capitalization?(meth)
167
+ CfnDsl::Errors.error(error, 1)
168
+ end
169
+
170
+ def incorrect_capitalization?(method)
171
+ method != titleize(method) && respond_to?(titleize(method))
172
+ end
173
+
174
+ def titleize(method)
175
+ method.to_s.clone.tap do |m|
176
+ m[0] = m[0, 1].upcase
177
+ end.to_sym
178
+ end
179
+ end
180
+
181
+ # Handles all of the Fn:: objects
182
+ class Fn < JSONable
183
+ def initialize(function, argument, refs = [])
184
+ @function = function
185
+ @argument = argument
186
+ @_refs = refs
187
+ end
188
+
189
+ def to_json(*a)
190
+ hash = {}
191
+ hash["Fn::#{@function}"] = @argument
192
+ hash.to_json(*a)
193
+ end
194
+
195
+ def references
196
+ @_refs
197
+ end
198
+
199
+ def ref_children
200
+ [@argument]
201
+ end
202
+ end
203
+
204
+ # Handles the Ref objects
205
+ class RefDefinition < JSONable
206
+ def initialize(value)
207
+ @Ref = value
208
+ end
209
+
210
+ def all_refs
211
+ [@Ref]
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,23 @@
1
+ require 'cfndsl/jsonable'
2
+
3
+ module CfnDsl
4
+ # Handles mapping objects
5
+ #
6
+ # Usage:
7
+ # Mapping("AWSRegionArch2AMI", {
8
+ # "us-east-1" => { "32" => "ami-6411e20d", "64" => "ami-7a11e213" },
9
+ # "us-west-1" => { "32" => "ami-c9c7978c", "64" => "ami-cfc7978a" },
10
+ # "eu-west-1" => { "32" => "ami-37c2f643", "64" => "ami-31c2f645" },
11
+ # "ap-southeast-1" => { "32" => "ami-66f28c34", "64" => "ami-60f28c32" },
12
+ # "ap-northeast-1" => { "32" => "ami-9c03a89d", "64" => "ami-a003a8a1" }
13
+ # })
14
+ class MappingDefinition < JSONable
15
+ def initialize(value)
16
+ @value = value
17
+ end
18
+
19
+ def to_json(*a)
20
+ @value.to_json(*a)
21
+ end
22
+ end
23
+ end