sfn 3.0.20 → 3.0.22

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fb7f6574c00a8cf09a14557610e3a00ea89e22d0
4
- data.tar.gz: e606f5fd3a2b9a9ba7b6ddad048a138d52259ea3
3
+ metadata.gz: 279982d00c24881c257eff78a3d14ed604952146
4
+ data.tar.gz: 9a093919f17154f3fc20920b933691f0438c0583
5
5
  SHA512:
6
- metadata.gz: 29704f3a0467a4e32a597b3565598852f5f38e15e6964d5f869930d46db46fbf0a9affea9605cbc8dac0f61267f1b63d2cb3c307b81e77d132d90f27932eda69
7
- data.tar.gz: 8985b3018da481d53f3214aa9302b58d5800730c94e16d42baa9b94ba9976b214ec7f759b80d4337ac9d51f19d45672e52acfdbe1ced3abefaa7ea3c48dffe1f
6
+ metadata.gz: 01741b34e7ef10214fa4f8902d2d35d1f175752d822f632e8f21e20ffdf5721e0a667fdf2f5ed1d5d56c87e87d3c8afb24319622bcbd62af70aa8e4c509b1a45
7
+ data.tar.gz: 57220d7bc625e74aa0b7f9f5ecbbc4fc22e77f56d11a3fda68bd1d5583ef4b84bb0084322496f7188b16d061212c51eeaf839c731290bc12e85ec523733a61ec
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # v3.0.22
2
+ * [fix] Properly match compile time parameters (#235)
3
+ * [fix] Remove AWS policy statements for undefined resources (#240)
4
+ * [enhancement] Support conditions when possible within planner (#230)
5
+ * [feature] Add alpha support for Terraform (#236)
6
+
1
7
  # v3.0.20
2
8
  * [fix] Only dump templates when dumpable (#220, #223)
3
9
  * [enhancement] Support NoEcho template parameters (#226)
data/README.md CHANGED
@@ -13,6 +13,7 @@ with orchestration APIs.
13
13
  * Heat
14
14
  * OpenStack
15
15
  * Rackspace
16
+ * Terraform
16
17
 
17
18
  ## Documentation
18
19
 
@@ -4,6 +4,7 @@ module Sfn
4
4
  module ApiProvider
5
5
 
6
6
  autoload :Google, 'sfn/api_provider/google'
7
+ autoload :Terraform, 'sfn/api_provider/terraform'
7
8
 
8
9
  end
9
10
  end
@@ -0,0 +1,71 @@
1
+ require 'sfn'
2
+
3
+ module Sfn
4
+ module ApiProvider
5
+
6
+ module Terraform
7
+
8
+ # Disable remote template storage
9
+ def store_template(*_)
10
+ end
11
+
12
+ # No formatting required on stack results
13
+ def format_nested_stack_results(*_)
14
+ {}
15
+ end
16
+
17
+ # Extract current parameters from parent template
18
+ #
19
+ # @param stack [SparkleFormation]
20
+ # @param stack_name [String]
21
+ # @param c_stack [Miasma::Models::Orchestration::Stack]
22
+ # @return [Hash]
23
+ def extract_current_nested_template_parameters(stack, stack_name, c_stack)
24
+ if(c_stack && c_stack.data[:parent_stack])
25
+ c_stack.data[:parent_stack].sparkleish_template(:remove_wrapper).fetch(
26
+ :resources, stack_name, :properties, :parameters, Smash.new
27
+ )
28
+ elsif(stack.parent)
29
+ val = stack.parent.compile.resources.set!(stack_name).properties
30
+ val.nil? ? Smash.new : val._dump
31
+ else
32
+ Smash.new
33
+ end
34
+ end
35
+
36
+ # Disable parameter validate as we can't adjust them without template modifications
37
+ def validate_stack_parameter(*_)
38
+ true
39
+ end
40
+
41
+ # Determine if parameter was set via intrinsic function
42
+ #
43
+ # @param val [Object]
44
+ # @return [TrueClass, FalseClass]
45
+ def function_set_parameter?(val)
46
+ if(val)
47
+ val.start_with?('${')
48
+ end
49
+ end
50
+
51
+ # Override requirement of nesting bucket
52
+ def validate_nesting_bucket!
53
+ true
54
+ end
55
+
56
+ # Override template content extraction to disable scrub behavior
57
+ #
58
+ # @param thing [SparkleFormation, Hash]
59
+ # @return [Hash]
60
+ def template_content(thing, *_)
61
+ if(thing.is_a?(SparkleFormation))
62
+ config[:sparkle_dump] ? thing.sparkle_dump : thing.dump
63
+ else
64
+ thing
65
+ end
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+ end
@@ -74,19 +74,28 @@ module Sfn
74
74
  # @param p_stack [Miasma::Models::Orchestration::Stack]
75
75
  # @return [NilClass]
76
76
  def save_stack_policy(p_stack)
77
+ valid_logical_ids = p_stack.resources.reload.all.map(&:logical_id)
78
+ stack_policy = @policies.fetch(p_stack.id,
79
+ @policies.fetch(p_stack.data[:logical_id]),
80
+ @policies[p_stack.name]
81
+ ).to_smash
82
+ if(stack_policy)
83
+ stack_policy[:statement].delete_if do |policy_item|
84
+ policy_match = policy_item[:resource].to_s.match(
85
+ %r{LogicalResourceId/(?<logical_id>.+)$}
86
+ )
87
+ if(policy_match)
88
+ !valid_logical_ids.include?(policy_match["logical_id"])
89
+ end
90
+ end
91
+ end
77
92
  result = p_stack.api.request(
78
93
  :path => '/',
79
94
  :method => :post,
80
95
  :form => Smash.new(
81
96
  'Action' => 'SetStackPolicy',
82
97
  'StackName' => p_stack.id,
83
- 'StackPolicyBody' => MultiJson.dump(
84
- @policies.fetch(p_stack.id,
85
- @policies.fetch(p_stack.data[:logical_id],
86
- @policies[p_stack.name]
87
- )
88
- )
89
- )
98
+ 'StackPolicyBody' => MultiJson.dump(stack_policy)
90
99
  )
91
100
  )
92
101
  end
@@ -83,7 +83,7 @@ Configuration.new do
83
83
  # nesting_prefix 'nested-templates'
84
84
  # Remote provider credentials
85
85
  credentials do
86
- # Remote provider name (:aws, :azure, :google, :open_stack, :rackspace)
86
+ # Remote provider name (:aws, :azure, :google, :open_stack, :rackspace, :terraform)
87
87
  provider :aws
88
88
  # AWS credentials information
89
89
  aws_access_key_id ENV['AWS_ACCESS_KEY_ID']
@@ -123,6 +123,14 @@ Configuration.new do
123
123
  google_service_account_email ENV['GOOGLE_SERVICE_ACCOUNT_EMAIL']
124
124
  google_service_account_private_key ENV['GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY']
125
125
  google_project ENV['GOOGLE_PROJECT']
126
+ # Terraform credentials information
127
+ # Valid driver names: :tfe, :boule, :local
128
+ terraform_driver :local
129
+ terraform_tfe_endpoint ENV['TFE_URL']
130
+ terraform_tfe_token ENV['TFE_TOKEN']
131
+ terraform_boule_endpoint ENV['BOULE_URL']
132
+ terraform_local_directory './terraform-stacks'
133
+ terraform_local_scrub_destroyed false
126
134
  end
127
135
  end
128
136
  EOF
@@ -30,8 +30,14 @@ module Sfn
30
30
  stack = provider.connection.stacks.get(stack_name)
31
31
  if(stack)
32
32
  nested_stack_cleanup!(stack)
33
- api_action!(:api_stack => stack) do
34
- stack.destroy
33
+ begin
34
+ api_action!(:api_stack => stack) do
35
+ stack.destroy
36
+ end
37
+ rescue Miasma::Error::ApiError::RequestError => error
38
+ raise unless error.response.code == 404
39
+ # if stack is already gone, disable polling
40
+ config[:poll] = false
35
41
  end
36
42
  ui.info "Destroy request complete for stack: #{ui.color(stack_name, :red)}"
37
43
  else
@@ -21,9 +21,7 @@ module Sfn
21
21
  config[:print_only] = true
22
22
  validate_graph_style!
23
23
  file = load_template_file
24
- file = parameter_scrub!(file.sparkle_dump)
25
24
  @outputs = Smash.new
26
- file = file.to_smash
27
25
  ui.info "Template resource graph generation - Style: #{ui.color(config[:graph_style], :bold)}"
28
26
  if(config[:file])
29
27
  ui.puts " -> path: #{config[:file]}"
@@ -38,14 +36,14 @@ module Sfn
38
36
  end
39
37
  graph = nil
40
38
  run_action 'Generating resource graph' do
41
- graph = generate_graph(file.to_smash)
39
+ graph = generate_graph(file)
42
40
  nil
43
41
  end
44
42
  run_action 'Writing graph result' do
45
43
  FileUtils.mkdir_p(File.dirname(config[:output_file]))
46
44
  if(config[:output_type] == 'dot')
47
- File.open("#{config[:output_file]}.dot", 'w') do |file|
48
- file.puts graph.to_s
45
+ File.open("#{config[:output_file]}.dot", 'w') do |o_file|
46
+ o_file.puts graph.to_s
49
47
  end
50
48
  else
51
49
  graph.save config[:output_file], config[:output_type]
@@ -74,10 +72,11 @@ module Sfn
74
72
  end
75
73
 
76
74
  def output_discovery(template, outputs, resource_name, parent_template, name='')
77
- if(template['Resources'])
78
- template['Resources'].each_pair do |r_name, r_info|
79
- if(r_info['Type'] == 'AWS::CloudFormation::Stack')
80
- output_discovery(r_info['Properties']['Stack'], outputs, r_name, template, r_name)
75
+ unless(template.resources.nil?)
76
+ template.resources.keys!.each do |r_name|
77
+ r_info = template.resources[r_name]
78
+ if(r_info.type == template._self.stack_resource_name)
79
+ output_discovery(r_info.properties.stack, outputs, r_name, template, r_name)
81
80
  end
82
81
  end
83
82
  end
@@ -69,7 +69,6 @@ module Sfn
69
69
  end
70
70
  end
71
71
  ui.info " -> #{stack_info}"
72
-
73
72
  if(file)
74
73
  if(config[:print_only])
75
74
  ui.puts format_json(parameter_scrub!(template_content(file)))
@@ -16,6 +16,8 @@ module Sfn
16
16
  TEMPLATE_PARAMETER_DEFAULTS = ['Default', 'defaultValue', 'default']
17
17
  # Template parameter no echo locations
18
18
  TEMPLATE_PARAMETER_NOECHO = ['NoEcho']
19
+ # Template parameter no echo custom
20
+ TEMPLATE_PARAMETER_SFN_NOECHO = ['Quiet', 'quiet']
19
21
 
20
22
  # Apply any defined remote stacks
21
23
  #
@@ -143,16 +145,19 @@ module Sfn
143
145
  # @param parameter_prefix [Array<String>] nesting prefix names
144
146
  # @param parameter_name [String] parameter name
145
147
  # @return [Array<String>] [expected_template_key, configuration_used_key]
146
- def locate_config_parameter_key(parameter_prefix, parameter_name)
148
+ def locate_config_parameter_key(parameter_prefix, parameter_name, root_name)
147
149
  check_name = parameter_name.downcase.tr('-_', '')
148
150
  check_prefix = parameter_prefix.map{|i| i.downcase.tr('-_', '') }
149
151
  key_match = config[:parameters].keys.detect do |cp_key|
150
152
  cp_key = cp_key.to_s.downcase.split('__').map{|i| i.tr('-_', '') }.join('__')
151
- cp_key.start_with?(check_prefix.join('__')) &&
152
- cp_key.split('__').last == check_name
153
+ non_root_matcher = (check_prefix + [check_name]).join('__')
154
+ root_matcher = ([root_name] + check_prefix + [check_name]).join('__')
155
+ cp_key == non_root_matcher ||
156
+ cp_key == root_matcher
153
157
  end
154
158
  actual_key = (parameter_prefix + [parameter_name]).compact.join('__')
155
159
  if(key_match)
160
+ ui.debug "Remapping configuration runtime parameter `#{key_match}` -> `#{actual_key}`"
156
161
  config[:parameters][actual_key] = config[:parameters].delete(key_match)
157
162
  end
158
163
  actual_key
@@ -186,12 +191,19 @@ module Sfn
186
191
  )
187
192
  )
188
193
  if(config[:interactive_parameters])
194
+ no_echo = !!TEMPLATE_PARAMETER_NOECHO.detect{|loc_key|
195
+ param_value[loc_key].to_s.downcase == 'true'
196
+ }
197
+ sfn_no_echo = TEMPLATE_PARAMETER_SFN_NOECHO.map do |loc_key|
198
+ res = param_value.delete(loc_key).to_s.downcase
199
+ res if !res.empty? && res != 'false'
200
+ end.compact.first
201
+ no_echo = sfn_no_echo if sfn_no_echo
189
202
  answer = ui.ask_question(
190
203
  "#{param_name.split(/([A-Z]+[^A-Z]*)/).find_all{|s|!s.empty?}.join(' ')}",
191
204
  :default => default,
192
- :no_echo => !!TEMPLATE_PARAMETER_NOECHO.detect{|loc_key|
193
- param_value[loc_key].to_s.downcase == 'true'
194
- }
205
+ :hide_default => sfn_no_echo == 'all',
206
+ :no_echo => !!no_echo
195
207
  )
196
208
  else
197
209
  answer = default
@@ -221,21 +233,25 @@ module Sfn
221
233
  # @option opts [Miasma::Models::Orchestration::Stack] :stack existing stack
222
234
  # @return [Hash]
223
235
  def populate_parameters!(sparkle, opts={})
224
- current_parameters = opts.fetch(:current_parameters, {})
236
+ current_parameters = opts[:current_parameters] || {}
225
237
  current_stack = opts[:stack]
226
238
  parameter_prefix, stack_parameters = prefix_parameters_setup(sparkle)
227
239
  unless(stack_parameters.empty?)
228
240
  format_config_parameters!
229
241
  param_banner = false
230
242
  stack_parameters.each do |param_name, param_value|
231
- ns_key = locate_config_parameter_key(parameter_prefix, param_name)
243
+ ns_key = locate_config_parameter_key(parameter_prefix, param_name, sparkle.root.name)
232
244
  # When parameter is a hash type, it is being set via
233
245
  # intrinsic function and we don't modify
234
246
  if(function_set_parameter?(current_parameters[param_name]))
235
- if(current_stack)
236
- enable_set = validate_stack_parameter(current_stack, param_name, ns_key, current_parameters[param_name])
247
+ if(!config[:parameters][ns_key].nil?)
248
+ ui.warn "Overriding mapped parameter value with explicit assignment `#{ns_key}`!"
237
249
  else
238
- enable_set = true
250
+ if(current_stack)
251
+ enable_set = validate_stack_parameter(current_stack, param_name, ns_key, current_parameters[param_name])
252
+ else
253
+ enable_set = true
254
+ end
239
255
  end
240
256
  if(enable_set)
241
257
  # NOTE: direct set dumps the stack (nfi). Smash will
@@ -180,13 +180,30 @@ module Sfn
180
180
  else
181
181
  current_state = compile_state.fetch(f_name, Smash.new)
182
182
  end
183
+
184
+ # NOTE: Prevent nesting stack compile state within stack compile state
185
+ current_state.delete("#{f_name}__#{f_name}")
186
+
183
187
  if(formation.compile_state)
184
188
  current_state = current_state.merge(formation.compile_state)
185
189
  end
186
190
  unless(formation.parameters.empty?)
187
191
  ui.info "#{ui.color('Compile time parameters:', :bold)} - template: #{ui.color(pathed_name, :green, :bold)}" unless config[:print_only]
188
192
  formation.parameters.each do |k,v|
189
- current_state[k] = request_compile_parameter(k, v, current_state[k], !!formation.parent)
193
+ valid_keys = [
194
+ "#{f_name}__#{k}",
195
+ Bogo::Utility.camel("#{f_name}__#{k}").downcase,
196
+ k,
197
+ Bogo::Utility.camel(k).downcase
198
+ ]
199
+ current_value = valid_keys.map do |key|
200
+ current_state[key]
201
+ end.compact.first
202
+ primary_key, secondary_key = ["#{f_name}__#{k}", k]
203
+ current_state[k] = request_compile_parameter(k, v,
204
+ current_value,
205
+ !!formation.parent
206
+ )
190
207
  end
191
208
  formation.compile_state = current_state
192
209
  end
@@ -231,19 +248,19 @@ module Sfn
231
248
  # core parameter set
232
249
  def merge_compile_time_parameters
233
250
  compile_state = config.fetch(:compile_parameters, Smash.new)
251
+ ui.debug "Initial compile parameters - #{compile_state}"
234
252
  compile_state.keys.each do |cs_key|
235
- if(cs_key.to_s.start_with?("#{arguments.first}__"))
236
- cli_provided = compile_state.delete(cs_key.to_s.sub("#{arguments.first.to_s}__", ''))
237
- if(cli_provided)
238
- compile_state[cs_key].deep_merge!(cli_provided)
253
+ unless(cs_key.to_s.start_with?("#{arguments.first}__"))
254
+ named_cs_key = "#{arguments.first}__#{cs_key}"
255
+ non_named = compile_state.delete(cs_key)
256
+ if(non_named && !compile_state.key?(named_cs_key))
257
+ ui.debug "Setting non-named compile parameter `#{cs_key}` into `#{named_cs_key}`"
258
+ compile_state[named_cs_key] = non_named
259
+ else
260
+ ui.debug "Discarding non-named compile parameter due to set named - `#{cs_key}` </> `#{named_cs_key}`"
239
261
  end
240
262
  end
241
263
  end
242
- compile_state.keys.each do |cs_key|
243
- unless(cs_key.start_with?("#{arguments.first}__"))
244
- compile_state["#{arguments.first}__#{cs_key}"] = compile_state.delete(cs_key)
245
- end
246
- end
247
264
  ui.debug "Merged compile parameters - #{compile_state}"
248
265
  compile_state
249
266
  end
@@ -82,13 +82,22 @@ module Sfn
82
82
  result = Smash.new
83
83
  v.split(',').each do |item_pair|
84
84
  key, value = item_pair.split(/[=:]/, 2)
85
- key = key.split('__')
86
- key = [key.pop, key.join('__')].reverse
87
- result.set(*key, value)
85
+ result[key] = value
88
86
  end
89
87
  result
90
88
  when Hash
91
- v.to_smash
89
+ result = Smash.new
90
+ extractor = lambda do |data, prefix|
91
+ data.each_pair do |key, value|
92
+ local_key = "#{prefix}__#{key}"
93
+ if(value.is_a?(Hash))
94
+ extractor.call(value, local_key)
95
+ else
96
+ result[local_key] = data
97
+ end
98
+ end
99
+ end
100
+ result
92
101
  else
93
102
  v
94
103
  end
@@ -14,6 +14,8 @@ module Sfn
14
14
  REF_MAPPING = {}
15
15
  FN_MAPPING = {}
16
16
 
17
+ UNKNOWN_RUNTIME_RESULT = '__UNKNOWN_RUNTIME_RESULT__'
18
+
17
19
  # @return [Array<String>] flagged items for value replacement
18
20
  attr_reader :flagged
19
21
 
@@ -23,6 +25,11 @@ module Sfn
23
25
  @flagged = []
24
26
  end
25
27
 
28
+ # @return [Hash] defined conditions
29
+ def conditions
30
+ @original.fetch('Conditions', {})
31
+ end
32
+
26
33
  # Flag a reference as modified
27
34
  #
28
35
  # @param ref_name [String]
@@ -48,30 +55,25 @@ module Sfn
48
55
  # @note also allows 'Ref' within funcs to provide mapping
49
56
  # replacements using the REF_MAPPING constant
50
57
  def apply_function(hash, funcs=[])
51
- k,v = hash.first
52
- if(hash.size == 1 && (k.start_with?('Fn') || k == 'Ref') && (funcs.empty? || funcs.include?(k)))
53
- case k
54
- when 'Fn::Join'
55
- v.last.join(v.first)
56
- when 'Fn::FindInMap'
57
- map_holder = mappings[v[0]]
58
- if(map_holder)
59
- map_item = map_holder[dereference(v[1])]
60
- if(map_item)
61
- map_item[v[2]]
58
+ if(hash.is_a?(Hash))
59
+ k,v = hash.first
60
+ if(hash.size == 1 && (k.start_with?('Fn') || k == 'Ref') && (funcs.include?(:all) || funcs.empty? || funcs.include?(k) || funcs == ['DEREF']))
61
+ method_name = Bogo::Utility.snake(k.gsub('::', ''))
62
+ if((funcs.include?(k) || funcs.include?(:all)) && respond_to?(method_name))
63
+ apply_function(send(method_name, v), funcs)
64
+ else
65
+ case k
66
+ when 'Fn::GetAtt'
67
+ funcs.include?('DEREF') ? dereference(hash) : hash
68
+ when 'Ref'
69
+ if(funcs.include?('DEREF'))
70
+ dereference(hash)
71
+ else
72
+ {'Ref' => self.class.const_get(:REF_MAPPING).fetch(v, v)}
73
+ end
62
74
  else
63
- raise "Failed to find mapping item! (#{v[0]} -> #{v[1]})"
75
+ hash
64
76
  end
65
- else
66
- raise "Failed to find mapping! (#{v[0]})"
67
- end
68
- when 'Fn::GetAtt'
69
- func.include?('DEREF') ? dereference(hash) : hash
70
- when 'Ref'
71
- if(funcs.include?('DEREF'))
72
- dereference(hash)
73
- else
74
- {'Ref' => self.class.const_get(:REF_MAPPING).fetch(v, v)}
75
77
  end
76
78
  else
77
79
  hash
@@ -81,6 +83,113 @@ module Sfn
81
83
  end
82
84
  end
83
85
 
86
+ # Evaluate given condition
87
+ #
88
+ # @param name [String] condition name
89
+ # @return [TrueClass, FalseClass]
90
+ def apply_condition(name)
91
+ condition = conditions[name]
92
+ if(condition)
93
+ apply_function(condition, [:all, 'DEREF'])
94
+ else
95
+ raise "Failed to locate condition with name `#{name}`!"
96
+ end
97
+ end
98
+
99
+ # Evaluate `if` conditional
100
+ #
101
+ # @param value [Array] {0: condition name, 1: true value, 2: false value}
102
+ # @return [Object] true or false value
103
+ def fn_if(value)
104
+ result = apply_condition(value[0])
105
+ if(result != UNKNOWN_RUNTIME_RESULT)
106
+ result ? value[1] : value[2]
107
+ else
108
+ UNKNOWN_RUNTIME_RESULT
109
+ result
110
+ end
111
+ end
112
+
113
+ # Determine if all conditions are true
114
+ #
115
+ # @param value [Array<String>] condition names
116
+ # @return [TrueClass, FalseClass]
117
+ def fn_and(value)
118
+ result = value.map do |val|
119
+ apply_condition(val)
120
+ end
121
+ if(result.to_s.include?(RUNTIME_MODIFIED))
122
+ UNKNOWN_RUNTIME_RESULT
123
+ else
124
+ result.all?
125
+ end
126
+ end
127
+
128
+ # Determine if any values are true
129
+ #
130
+ # @param value [Array<String>] condition names
131
+ # @return [TrueClass, FalseClass]
132
+ def fn_or(value)
133
+ result = value.map do |val|
134
+ apply_condition(val)
135
+ end
136
+ if(result.to_s.include?(RUNTIME_MODIFIED))
137
+ UNKNOWN_RUNTIME_RESULT
138
+ else
139
+ result.any?
140
+ end
141
+ end
142
+
143
+ # Negate given value
144
+ #
145
+ # @param value [Array<Hash>]
146
+ # @return [TrueClass, FalseClass]
147
+ def fn_not(value)
148
+ result = apply_function(value)
149
+ result == RUNTIME_MODIFIED ? UNKNOWN_RUNTIME_RESULT : !result
150
+ end
151
+
152
+ # Determine if values are equal
153
+ #
154
+ # @param value [Array] values
155
+ # @return [TrueClass, FalseClass]
156
+ def fn_equals(value)
157
+ value = value.map do |val|
158
+ apply_function(val)
159
+ end
160
+ if(value.to_s.include?(RUNTIME_MODIFIED))
161
+ UNKNOWN_RUNTIME_RESULT
162
+ else
163
+ value.first == value.last
164
+ end
165
+ end
166
+
167
+ # Join values with given delimiter
168
+ #
169
+ # @param value [Array<String,Array<String>>]
170
+ # @return [String]
171
+ def fn_join(value)
172
+ value.last.join(value.first)
173
+ end
174
+
175
+ # Lookup value in mappings
176
+ #
177
+ # @param value [Array]
178
+ # @return [String, Fixnum]
179
+ def fn_find_in_map(value)
180
+ map_holder = mappings[value[0]]
181
+ if(map_holder)
182
+ map_item = map_holder[dereference(value[1])]
183
+ if(map_item)
184
+ map_item[value[2]]
185
+ else
186
+ raise "Failed to find mapping item! (#{value[0]} -> #{value[1]})"
187
+ end
188
+ else
189
+ raise "Failed to find mapping! (#{value[0]})"
190
+ end
191
+ end
192
+
84
193
  # Override the parent dereference behavior to return junk
85
194
  # value on flagged resource match
86
195
  #
@@ -416,15 +525,19 @@ module Sfn
416
525
  ]
417
526
  )
418
527
  begin
419
- r_info = SparkleFormation::Resources::Aws.resource_lookup(type)
420
- r_property = r_info.property(property_name)
421
- if(r_property)
422
- effect = r_property.update_causes(
423
- templates.get(:update, 'Resources', resource_name),
424
- templates.get(:origin, 'Resources', resource_name)
425
- )
528
+ if(templates.get(:update, 'Resources', resource_name, 'Properties', property_name) == Translator::UNKNOWN_RUNTIME_RESULT)
529
+ effect = :unknown
426
530
  else
427
- raise KeyError.new 'Unknown property'
531
+ r_info = SparkleFormation::Resources::Aws.resource_lookup(type)
532
+ r_property = r_info.property(property_name)
533
+ if(r_property)
534
+ effect = r_property.update_causes(
535
+ templates.get(:update, 'Resources', resource_name),
536
+ templates.get(:origin, 'Resources', resource_name)
537
+ )
538
+ else
539
+ raise KeyError.new 'Unknown property'
540
+ end
428
541
  end
429
542
  case effect.to_sym
430
543
  when :replacement
@@ -507,19 +620,30 @@ module Sfn
507
620
  template.keys.each do |t_key|
508
621
  next if ['Outputs', 'Resources'].include?(t_key)
509
622
  template[t_key] = translator.dereference_processor(
510
- template[t_key], ['Ref', 'Fn', 'DEREF', 'Fn::FindInMap']
623
+ template[t_key], ['DEREF']
511
624
  )
512
625
  end
513
626
  translator.original.replace(template)
514
- if(template['Resources'])
515
- template['Resources'] = translator.dereference_processor(
516
- template['Resources'], ['Ref', 'Fn', 'DEREF', 'Fn::FindInMap']
517
- )
627
+ ['Outputs', 'Resources'].each do |t_key|
628
+ if(template[t_key])
629
+ template[t_key] = translator.dereference_processor(
630
+ template[t_key], ['DEREF', :all]
631
+ )
632
+ end
518
633
  end
519
- if(template['Outputs'])
520
- template['Outputs'] = translator.dereference_processor(
521
- template['Outputs'], ['Ref', 'Fn', 'DEREF', 'Fn::FindInMap']
522
- )
634
+ if(template['Resources'])
635
+ valid_resources = template['Resources'].map do |resource_name, resource_value|
636
+ if(resource_value['OnCondition'])
637
+ if(translator.apply_condition(resource_value['OnCondition']))
638
+ resource_name
639
+ end
640
+ else
641
+ resource_name
642
+ end
643
+ end.compact
644
+ (template['Resources'].keys - valid_resources).each do |resource_to_remove|
645
+ template['Resources'].delete(resource_to_remove)
646
+ end
523
647
  end
524
648
  translator.original.replace({})
525
649
  template
data/lib/sfn/provider.rb CHANGED
@@ -78,7 +78,7 @@ module Sfn
78
78
  )
79
79
  @cache = args.fetch(:cache, Cache.new(:local))
80
80
  @async = args.fetch(:async, true)
81
- @miamsa_args = args[:miasma].dup
81
+ @miasma_args = args[:miasma].dup
82
82
  cache.init(:stacks_lock, :lock, :timeout => 0.1)
83
83
  cache.init(:stacks, :stamped)
84
84
  cache.init(:stack_expansion_lock, :lock, :timeout => 0.1)
data/lib/sfn/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Sfn
2
2
  # Current library version
3
- VERSION = Gem::Version.new('3.0.20')
3
+ VERSION = Gem::Version.new('3.0.22')
4
4
  end
data/sfn.gemspec CHANGED
@@ -11,20 +11,23 @@ Gem::Specification.new do |s|
11
11
  s.license = 'Apache-2.0'
12
12
  s.require_path = 'lib'
13
13
  s.add_runtime_dependency 'bogo-cli', '>= 0.2.5', '< 0.4'
14
- s.add_runtime_dependency 'bogo-ui', '>= 0.1.20', '< 0.4'
14
+ s.add_runtime_dependency 'bogo-ui', '>= 0.1.22', '< 0.4'
15
15
  s.add_runtime_dependency 'miasma', '>= 0.3.0', '< 0.4'
16
16
  s.add_runtime_dependency 'miasma-aws', '>= 0.3.6', '< 0.4'
17
17
  s.add_runtime_dependency 'miasma-azure', '>= 0.1.0', '< 0.3'
18
18
  s.add_runtime_dependency 'miasma-open-stack', '>= 0.1.0', '< 0.3'
19
19
  s.add_runtime_dependency 'miasma-rackspace', '>= 0.1.0', '< 0.3'
20
20
  s.add_runtime_dependency 'miasma-google', '>= 0.1.0', '< 0.3'
21
+ s.add_runtime_dependency 'miasma-terraform', '>= 0.1.0', '< 0.2.0'
21
22
  s.add_runtime_dependency 'jmespath'
22
23
  s.add_runtime_dependency 'net-ssh'
23
- s.add_runtime_dependency 'sparkle_formation', '>= 3.0.3', '< 4'
24
+ s.add_runtime_dependency 'sparkle_formation', '>= 3.0.11', '< 4'
24
25
  s.add_runtime_dependency 'hashdiff', '~> 0.2.2'
25
26
  s.add_runtime_dependency 'graph', '~> 2.8.1'
26
27
  s.add_development_dependency 'rake', '~> 10'
27
28
  s.add_development_dependency 'minitest'
29
+ s.add_development_dependency 'rspec', '~> 3.5'
30
+ s.add_development_dependency 'rubocop', '0.38.0'
28
31
  s.add_development_dependency 'mocha'
29
32
  s.add_development_dependency 'yard'
30
33
  s.executables << 'sfn'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sfn
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.20
4
+ version: 3.0.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Roberts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-02 00:00:00.000000000 Z
11
+ date: 2017-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bogo-cli
@@ -36,7 +36,7 @@ dependencies:
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: 0.1.20
39
+ version: 0.1.22
40
40
  - - "<"
41
41
  - !ruby/object:Gem::Version
42
42
  version: '0.4'
@@ -46,7 +46,7 @@ dependencies:
46
46
  requirements:
47
47
  - - ">="
48
48
  - !ruby/object:Gem::Version
49
- version: 0.1.20
49
+ version: 0.1.22
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
52
  version: '0.4'
@@ -170,6 +170,26 @@ dependencies:
170
170
  - - "<"
171
171
  - !ruby/object:Gem::Version
172
172
  version: '0.3'
173
+ - !ruby/object:Gem::Dependency
174
+ name: miasma-terraform
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: 0.1.0
180
+ - - "<"
181
+ - !ruby/object:Gem::Version
182
+ version: 0.2.0
183
+ type: :runtime
184
+ prerelease: false
185
+ version_requirements: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: 0.1.0
190
+ - - "<"
191
+ - !ruby/object:Gem::Version
192
+ version: 0.2.0
173
193
  - !ruby/object:Gem::Dependency
174
194
  name: jmespath
175
195
  requirement: !ruby/object:Gem::Requirement
@@ -204,7 +224,7 @@ dependencies:
204
224
  requirements:
205
225
  - - ">="
206
226
  - !ruby/object:Gem::Version
207
- version: 3.0.3
227
+ version: 3.0.11
208
228
  - - "<"
209
229
  - !ruby/object:Gem::Version
210
230
  version: '4'
@@ -214,7 +234,7 @@ dependencies:
214
234
  requirements:
215
235
  - - ">="
216
236
  - !ruby/object:Gem::Version
217
- version: 3.0.3
237
+ version: 3.0.11
218
238
  - - "<"
219
239
  - !ruby/object:Gem::Version
220
240
  version: '4'
@@ -274,6 +294,34 @@ dependencies:
274
294
  - - ">="
275
295
  - !ruby/object:Gem::Version
276
296
  version: '0'
297
+ - !ruby/object:Gem::Dependency
298
+ name: rspec
299
+ requirement: !ruby/object:Gem::Requirement
300
+ requirements:
301
+ - - "~>"
302
+ - !ruby/object:Gem::Version
303
+ version: '3.5'
304
+ type: :development
305
+ prerelease: false
306
+ version_requirements: !ruby/object:Gem::Requirement
307
+ requirements:
308
+ - - "~>"
309
+ - !ruby/object:Gem::Version
310
+ version: '3.5'
311
+ - !ruby/object:Gem::Dependency
312
+ name: rubocop
313
+ requirement: !ruby/object:Gem::Requirement
314
+ requirements:
315
+ - - '='
316
+ - !ruby/object:Gem::Version
317
+ version: 0.38.0
318
+ type: :development
319
+ prerelease: false
320
+ version_requirements: !ruby/object:Gem::Requirement
321
+ requirements:
322
+ - - '='
323
+ - !ruby/object:Gem::Version
324
+ version: 0.38.0
277
325
  - !ruby/object:Gem::Dependency
278
326
  name: mocha
279
327
  requirement: !ruby/object:Gem::Requirement
@@ -346,6 +394,7 @@ files:
346
394
  - lib/sfn.rb
347
395
  - lib/sfn/api_provider.rb
348
396
  - lib/sfn/api_provider/google.rb
397
+ - lib/sfn/api_provider/terraform.rb
349
398
  - lib/sfn/cache.rb
350
399
  - lib/sfn/callback.rb
351
400
  - lib/sfn/callback/aws_assume_role.rb
@@ -435,7 +484,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
435
484
  version: '0'
436
485
  requirements: []
437
486
  rubyforge_project:
438
- rubygems_version: 2.5.1
487
+ rubygems_version: 2.4.8
439
488
  signing_key:
440
489
  specification_version: 4
441
490
  summary: SparkleFormation CLI