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.
- checksums.yaml +5 -13
- data/.rubocop.yml +23 -0
- data/Gemfile +4 -0
- data/Rakefile +19 -17
- data/bin/cfndsl +20 -20
- data/cfndsl.gemspec +16 -15
- data/lib/cfndsl.rb +62 -68
- data/lib/cfndsl/aws/cloud_formation_template.rb +16 -0
- data/lib/cfndsl/aws/types.rb +12 -0
- data/lib/cfndsl/{aws_types.yaml → aws/types.yaml} +0 -0
- data/lib/cfndsl/{Conditions.rb → conditions.rb} +5 -7
- data/lib/cfndsl/creation_policy.rb +21 -0
- data/lib/cfndsl/errors.rb +29 -0
- data/lib/cfndsl/generate_types.rb +154 -0
- data/lib/cfndsl/jsonable.rb +214 -0
- data/lib/cfndsl/mappings.rb +23 -0
- data/lib/cfndsl/metadata.rb +16 -0
- data/lib/cfndsl/module.rb +52 -51
- data/lib/cfndsl/names.rb +5 -5
- data/lib/cfndsl/orchestration_template.rb +173 -0
- data/lib/cfndsl/os/heat_template.rb +16 -0
- data/lib/cfndsl/os/types.rb +12 -0
- data/lib/cfndsl/{os_types.yaml → os/types.yaml} +11 -11
- data/lib/cfndsl/{Outputs.rb → outputs.rb} +3 -4
- data/lib/cfndsl/{Parameters.rb → parameters.rb} +12 -13
- data/lib/cfndsl/plurals.rb +34 -0
- data/lib/cfndsl/properties.rb +21 -0
- data/lib/cfndsl/rake_task.rb +9 -7
- data/lib/cfndsl/ref_check.rb +44 -0
- data/lib/cfndsl/{Resources.rb → resources.rb} +13 -15
- data/lib/cfndsl/types.rb +151 -0
- data/lib/cfndsl/update_policy.rb +25 -0
- data/lib/cfndsl/version.rb +1 -1
- data/sample/autoscale.rb +152 -158
- data/sample/autoscale2.rb +151 -155
- data/sample/circular.rb +30 -33
- data/sample/codedeploy.rb +35 -36
- data/sample/config_service.rb +120 -0
- data/sample/ecs.rb +39 -39
- data/sample/iam_policies.rb +82 -0
- data/sample/lambda.rb +20 -24
- data/sample/s3.rb +11 -11
- data/sample/t1.rb +7 -9
- data/sample/vpc_example.rb +50 -0
- data/sample/vpc_with_vpn_example.rb +97 -0
- data/spec/cfndsl_spec.rb +22 -11
- data/spec/fixtures/heattest.rb +13 -14
- data/spec/fixtures/test.rb +56 -53
- metadata +36 -30
- data/lib/cfndsl/CloudFormationTemplate.rb +0 -267
- data/lib/cfndsl/CreationPolicy.rb +0 -25
- data/lib/cfndsl/Errors.rb +0 -31
- data/lib/cfndsl/JSONable.rb +0 -235
- data/lib/cfndsl/Mappings.rb +0 -25
- data/lib/cfndsl/Metadata.rb +0 -22
- data/lib/cfndsl/Plurals.rb +0 -35
- data/lib/cfndsl/Properties.rb +0 -25
- data/lib/cfndsl/RefCheck.rb +0 -48
- data/lib/cfndsl/Types.rb +0 -309
- data/lib/cfndsl/UpdatePolicy.rb +0 -29
- data/sample/config-service.rb +0 -119
- data/sample/iam-policies.rb +0 -82
- data/sample/vpc-example.rb +0 -51
- data/sample/vpc-with-vpn-example.rb +0 -97
File without changes
|
@@ -1,13 +1,11 @@
|
|
1
|
-
require 'cfndsl/
|
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
|