cfndsl 0.4.4 → 0.5.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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