stackmate 0.0.4 → 0.1.0

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 (66) hide show
  1. data/CHANGELOG.txt +7 -0
  2. data/README.md +25 -8
  3. data/bin/stackmate.rb +57 -53
  4. data/lib/stackmate/aws_attribs.rb +31 -31
  5. data/lib/stackmate/classmap.rb +33 -25
  6. data/lib/stackmate/client.rb +80 -0
  7. data/lib/stackmate/intrinsic_functions.rb +94 -38
  8. data/lib/stackmate/logging.rb +19 -19
  9. data/lib/stackmate/participants/cloudstack.rb +250 -165
  10. data/lib/stackmate/participants/cloudstack_affinitygroup.rb +122 -0
  11. data/lib/stackmate/participants/cloudstack_autoscalepolicy.rb +113 -0
  12. data/lib/stackmate/participants/cloudstack_autoscalevmgroup.rb +86 -0
  13. data/lib/stackmate/participants/cloudstack_autoscalevmprofile.rb +140 -0
  14. data/lib/stackmate/participants/cloudstack_condition.rb +122 -0
  15. data/lib/stackmate/participants/cloudstack_egressfirewallrule.rb +149 -0
  16. data/lib/stackmate/participants/cloudstack_firewallrule.rb +149 -0
  17. data/lib/stackmate/participants/cloudstack_globalloadbalancerrule.rb +158 -0
  18. data/lib/stackmate/participants/cloudstack_instancegroup.rb +113 -0
  19. data/lib/stackmate/participants/cloudstack_ipaddress.rb +149 -0
  20. data/lib/stackmate/participants/cloudstack_ipforwardingrule.rb +131 -0
  21. data/lib/stackmate/participants/cloudstack_iptonic.rb +95 -0
  22. data/lib/stackmate/participants/cloudstack_iso.rb +95 -0
  23. data/lib/stackmate/participants/cloudstack_lbhealthcheckpolicy.rb +140 -0
  24. data/lib/stackmate/participants/cloudstack_lbstickinesspolicy.rb +122 -0
  25. data/lib/stackmate/participants/cloudstack_loadbalancer.rb +158 -0
  26. data/lib/stackmate/participants/cloudstack_loadbalancerrule.rb +185 -0
  27. data/lib/stackmate/participants/cloudstack_network.rb +293 -0
  28. data/lib/stackmate/participants/cloudstack_networkacl.rb +176 -0
  29. data/lib/stackmate/participants/cloudstack_networkacllist.rb +104 -0
  30. data/lib/stackmate/participants/cloudstack_nictovirtualmachine.rb +104 -0
  31. data/lib/stackmate/participants/cloudstack_portforwardingrule.rb +176 -0
  32. data/lib/stackmate/participants/cloudstack_project.rb +113 -0
  33. data/lib/stackmate/participants/cloudstack_remoteaccessvpn.rb +122 -0
  34. data/lib/stackmate/participants/cloudstack_securitygroup.rb +122 -0
  35. data/lib/stackmate/participants/cloudstack_securitygroupegress.rb +185 -0
  36. data/lib/stackmate/participants/cloudstack_securitygroupingress.rb +185 -0
  37. data/lib/stackmate/participants/cloudstack_snapshot.rb +113 -0
  38. data/lib/stackmate/participants/cloudstack_snapshotpolicy.rb +122 -0
  39. data/lib/stackmate/participants/cloudstack_sshkeypair.rb +113 -0
  40. data/lib/stackmate/participants/cloudstack_staticnat.rb +113 -0
  41. data/lib/stackmate/participants/cloudstack_staticroute.rb +95 -0
  42. data/lib/stackmate/participants/cloudstack_tags.rb +113 -0
  43. data/lib/stackmate/participants/cloudstack_template.rb +212 -0
  44. data/lib/stackmate/participants/cloudstack_togloballoadbalancerrule.rb +104 -0
  45. data/lib/stackmate/participants/cloudstack_toloadbalancerrule.rb +95 -0
  46. data/lib/stackmate/participants/cloudstack_virtualmachine.rb +348 -0
  47. data/lib/stackmate/participants/cloudstack_virtualmachineops.rb +95 -0
  48. data/lib/stackmate/participants/cloudstack_vmsnapshot.rb +113 -0
  49. data/lib/stackmate/participants/cloudstack_volume.rb +176 -0
  50. data/lib/stackmate/participants/cloudstack_volumeops.rb +111 -0
  51. data/lib/stackmate/participants/cloudstack_vpc.rb +158 -0
  52. data/lib/stackmate/participants/cloudstack_vpnconnection.rb +95 -0
  53. data/lib/stackmate/participants/cloudstack_vpncustomergateway.rb +176 -0
  54. data/lib/stackmate/participants/cloudstack_vpngateway.rb +86 -0
  55. data/lib/stackmate/participants/cloudstack_vpnuser.rb +122 -0
  56. data/lib/stackmate/participants/common.rb +219 -70
  57. data/lib/stackmate/participants/stacknest.rb +6 -0
  58. data/lib/stackmate/resolver.rb +122 -0
  59. data/lib/stackmate/stack.rb +116 -60
  60. data/lib/stackmate/stack_executor.rb +99 -37
  61. data/lib/stackmate/stackpi.rb +46 -0
  62. data/lib/stackmate/version.rb +1 -1
  63. data/lib/stackmate/waitcondition_server.rb +15 -15
  64. data/lib/stackmate.rb +1 -1
  65. data/stackmate.gemspec +2 -4
  66. metadata +70 -19
@@ -1,91 +1,240 @@
1
+ require 'json'
2
+ require 'yaml'
1
3
  require 'stackmate/logging'
2
- require 'stackmate/aws_attribs'
3
4
  require 'stackmate/intrinsic_functions'
5
+ require 'stackmate/resolver'
6
+ require 'stackmate/stackpi'
7
+ require 'net/http'
8
+ require 'uri'
9
+ require 'time'
4
10
 
5
11
  module StackMate
6
12
 
7
13
 
8
- class WaitConditionHandle < Ruote::Participant
9
- include Logging
14
+ # class ParamHandle < Ruote::Participant
15
+ # include Logging
16
+ # def on_workitem
17
+ # logger.debug "Setting resolved parameter #{participant_name}"
18
+ # workitem['ResolvedNames'][participant_name] = workitem['']
19
+ # end
20
+ # end
21
+
22
+ class WaitConditionHandle < Ruote::Participant
23
+ include Logging
24
+
25
+ def create
26
+ logger.debug "Entering #{participant_name} "
27
+ workitem[participant_name] = {}
28
+ presigned_url = 'http://localhost:4567/waitcondition/' + workitem.fei.wfid + '/' + participant_name
29
+ workitem.fields['ResolvedNames'][participant_name] = presigned_url
30
+ logger.info "Your pre-signed URL is: #{presigned_url} "
31
+ logger.info "Try: \ncurl -X PUT --data 'foo' #{presigned_url}"
32
+ WaitCondition.create_handle(participant_name, presigned_url)
33
+ workitem[participant_name][:physical_id] = presigned_url
34
+ end
10
35
 
11
- def on_workitem
12
- logger.debug "Entering #{participant_name} "
13
- workitem[participant_name] = {}
14
- presigned_url = 'http://localhost:4567/waitcondition/' + workitem.fei.wfid + '/' + participant_name
15
- workitem.fields['ResolvedNames'][participant_name] = presigned_url
16
- logger.info "Your pre-signed URL is: #{presigned_url} "
17
- logger.info "Try: \ncurl -X PUT --data 'foo' #{presigned_url}"
18
- WaitCondition.create_handle(participant_name, presigned_url)
19
- workitem[participant_name][:physical_id] = presigned_url
36
+ def delete
37
+ #p workitem
38
+ logger.info "In delete #{participant_name}"
39
+ return nil if !workitem[participant_name]
40
+ physical_id = workitem[participant_name][:physical_id]
41
+ if physical_id
42
+ workitem[participant_name] = {}
43
+ WaitCondition.delete_handle(participant_name)
44
+ end
45
+ end
20
46
 
21
- reply
22
- end
23
- end
24
-
25
- class WaitCondition < Ruote::Participant
26
- include Logging
27
- @@handles = {}
28
- @@conditions = []
29
-
30
- def on_workitem
31
- logger.debug "Entering #{workitem.participant_name} "
32
- workitem[participant_name] = {}
33
- @@conditions << self
34
- stackname = workitem.fields['ResolvedNames']['AWS::StackName']
35
- workitem[participant_name][:physical_id] = stackname + '-' + 'WaitCondition'
47
+ def on_workitem
48
+ if workitem['params']['operation'] == 'create'
49
+ create
50
+ else
51
+ #rollback / delete
52
+ delete
53
+ end
54
+ reply
55
+ end
36
56
  end
37
57
 
38
- def self.create_handle(handle_name, handle)
58
+ class WaitCondition < Ruote::Participant
59
+ include Logging
60
+ @@handles = {}
61
+ @@conditions = []
62
+
63
+ def create
64
+ logger.debug "Entering #{workitem.participant_name} "
65
+ workitem[participant_name] = {}
66
+ @@conditions << self
67
+ stackname = workitem.fields['ResolvedNames']['AWS::StackName']
68
+ workitem[participant_name][:physical_id] = stackname + '-' + 'WaitCondition'
69
+ end
70
+
71
+ def delete
72
+ logger.info "In delete #{participant_name}"
73
+ #no-op
74
+ end
75
+
76
+ def on_workitem
77
+ if workitem['params']['operation'] == 'create'
78
+ create
79
+ else
80
+ #rollback / delete
81
+ delete
82
+ reply
83
+ end
84
+ end
85
+
86
+ def self.create_handle(handle_name, handle)
39
87
  @@handles[handle_name] = handle
40
- end
88
+ end
89
+
90
+ def self.delete_handle(handle_name)
91
+ @@handles.delete(handle_name)
92
+ end
41
93
 
42
- def set_handle(handle_name)
94
+ def set_handle(handle_name)
43
95
  reply(workitem) if @@handles[handle_name]
44
- end
96
+ end
45
97
 
46
- def self.get_conditions()
98
+ def self.get_conditions()
47
99
  @@conditions
100
+ end
101
+ end
102
+
103
+ class Output < Ruote::Participant
104
+ include Logging
105
+ include Intrinsic
106
+
107
+ def on_workitem
108
+ #p workitem.fields.keys
109
+ logger.debug "Entering #{workitem.participant_name} "
110
+ outputs = workitem.fields['Outputs']
111
+ logger.debug "In StackMate::Output.on_workitem #{outputs.inspect}"
112
+ outputs.each do |key, val|
113
+ v = val['Value']
114
+ constructed_value = intrinsic(v, workitem)
115
+ val['Value'] = constructed_value
116
+ logger.debug "Output: key = #{key}, value = #{constructed_value} descr = #{val['Description']}"
117
+ end
118
+
119
+ logger.debug "Output Done"
120
+ reply
121
+ end
48
122
  end
49
- end
50
-
51
- class Output < Ruote::Participant
52
- include Logging
53
- include Intrinsic
54
-
55
- def on_workitem
56
- #p workitem.fields.keys
57
- logger.debug "Entering #{workitem.participant_name} "
58
- outputs = workitem.fields['Outputs']
59
- logger.debug "In StackMate::Output.on_workitem #{outputs.inspect}"
60
- outputs.each do |key, val|
61
- v = val['Value']
62
- constructed_value = intrinsic(v, workitem)
63
- val['Value'] = constructed_value
64
- logger.debug "Output: key = #{key}, value = #{constructed_value} descr = #{val['Description']}"
65
- end
66
-
67
- logger.debug "Output Done"
68
- reply
123
+
124
+ class NoOpResource < Ruote::Participant
125
+ include Logging
126
+
127
+ def create
128
+ logger.debug "Creating #{participant_name} wfid=#{workitem.fei.wfid} fei=#{workitem.fei.to_h}"
129
+ workitem[participant_name] = {}
130
+ stackname = workitem.fields['ResolvedNames']['AWS::StackName']
131
+ logger.debug "physical id is #{stackname}-#{participant_name} "
132
+ workitem[participant_name][:physical_id] = stackname + '-' + participant_name
133
+ typ = workitem['Resources'][participant_name]['Type']
134
+ if AWS_FAKE_ATTRIB_VALUES[typ]
135
+ AWS_FAKE_ATTRIB_VALUES[typ].each do |k,v|
136
+ workitem[participant_name][k] = v
137
+ end
138
+ end
139
+ end
140
+
141
+ def delete
142
+ logger.debug "Deleting #{participant_name} wfid=#{workitem.fei.wfid} fei=#{workitem.fei.to_h}"
143
+ end
144
+
145
+ def on_workitem
146
+ @stackname = workitem['StackName']
147
+ if workitem['params']['operation'] == 'create'
148
+ create
149
+ else
150
+ delete
151
+ end
152
+ reply
153
+ end
69
154
  end
70
- end
71
-
72
- class NoOpResource < Ruote::Participant
73
- include Logging
74
-
75
- def on_workitem
76
- logger.debug "Entering #{participant_name} wfid=#{workitem.fei.wfid} fei=#{workitem.fei.to_h}"
77
- workitem[participant_name] = {}
78
- stackname = workitem.fields['ResolvedNames']['AWS::StackName']
79
- logger.debug "physical id is #{stackname}-#{participant_name} "
80
- workitem[participant_name][:physical_id] = stackname + '-' + participant_name
81
- typ = workitem['Resources'][participant_name]['Type']
82
- if AWS_FAKE_ATTRIB_VALUES[typ]
83
- AWS_FAKE_ATTRIB_VALUES[typ].each do |k,v|
84
- workitem[participant_name][k] = v
155
+
156
+ class StackNest < Ruote::Participant
157
+ include Logging
158
+ include Intrinsic
159
+ include Resolver
160
+ include StackPi
161
+
162
+ def create
163
+ logger.debug("Creating nested stack #{@stackname}")
164
+ #Get template from URL
165
+ #Call stackmate API for launching new stack
166
+ #read outputs from predefined location
167
+ #copy all critical fields from workitems
168
+
169
+ #CAVEAT - needs file storage. may be move to database backed
170
+ # needs a Outputs tag. Not a big deal since nested stacks anyways need
171
+ #outputs tags
172
+ workitem[@stack_name] = {}
173
+ params = workitem['ResolvedNames']
174
+ stack_props = workitem['Resources'][@stack_name]['Properties']
175
+ template_url = URI(get_resolved(stack_props['TemplateURL'],workitem))
176
+ logger.debug("Fetching template for #{@stack_name} from URL #{template_url}")
177
+ http = Net::HTTP.new(template_url.host,template_url.port)
178
+ if template_url.scheme == 'https'
179
+ http.use_ssl = true
180
+ end
181
+ http.start {
182
+ http.request_get(template_url.path) { |res|
183
+ File.open("/tmp/#{@stack_name}.template", 'w') { |file| file.write(res.body) }
184
+ }
185
+ }
186
+ stack_props['Parameters'].each_key do |k|
187
+ params[k] = get_resolved(stack_props['Parameters'][k],workitem)
188
+ end
189
+ params['stackrand'] = Time.now().to_i #TODO use subid from fei or something
190
+ params['isnested'] = "True"
191
+ StackMate::StackPi.create_stack("/tmp/#{@stack_name}.template",@stack_name,format_params(params),true)
192
+ file_name = "/tmp/#{@stack_name}.workitem.#{params['stackrand']}"
193
+ if(File.exists?(file_name))
194
+ output_workitem = YAML.load(File.read(file_name))
195
+ workitem[@stack_name]['ResolvedNames'] = output_workitem['ResolvedNames'].clone
196
+ workitem[@stack_name]['Outputs'] = output_workitem['Outputs'].clone
197
+ workitem[@stack_name]['Resources'] = output_workitem['Resources'].clone
198
+ workitem[@stack_name]['IdMap'] = output_workitem['IdMap'].clone
199
+ #copy all resources created
200
+ output_workitem['Resources'].each_key do |resource|
201
+ workitem[@stack_name][resource] = output_workitem[resource].clone
202
+ end
203
+ logger.debug("Successfully created nested stack #{@stack_name}")
204
+ else
205
+ logger.debug("Unable to create nested stack #{@stack_name}")
206
+ raise "Nested Stack Failed"
207
+ end
208
+ end
209
+
210
+ def delete
211
+ logger.debug("Deleting stack #{@stack_name}")
212
+ #TODO write code to roll back all stack info
213
+ #Probably nothing needed since new engine launched takes care of it
214
+ #No, need to clean up if parent stack fails after the nested stack is successfully created
215
+ end
216
+
217
+ def on_workitem
218
+ @stack_name = workitem.participant_name
219
+ if workitem['params']['operation'] == 'create'
220
+ create
221
+ else
222
+ #rollback / delete
223
+ delete
224
+ end
225
+ reply
226
+ end
227
+
228
+ def format_params(params)
229
+ result = ""
230
+ add_semi = false
231
+ params.each_key do |k|
232
+ result = result + ";" if add_semi
233
+ result = result + k + "=" + params[k].to_s if !params[k].nil?
234
+ add_semi = true
85
235
  end
236
+ result
86
237
  end
87
- reply
88
238
  end
89
- end
90
239
 
91
- end
240
+ end
@@ -0,0 +1,6 @@
1
+ require 'json'
2
+ require 'cloudstack_ruby_client'
3
+ require 'yaml'
4
+ require 'stackmate/logging'
5
+ require 'stackmate/intrinsic_functions'
6
+ require 'stackmate/resolver'
@@ -0,0 +1,122 @@
1
+ #require 'stackmate/logging'
2
+ require 'stackmate/intrinsic_functions'
3
+
4
+ module StackMate
5
+ module Resolver
6
+ @devicename_map=
7
+ {
8
+ '/dev/sdb' => '1',
9
+ '/dev/sdc' => '2',
10
+ '/dev/sdd' => '3',
11
+ '/dev/sde' => '4',
12
+ '/dev/sdf' => '5',
13
+ '/dev/sdg' => '6',
14
+ '/dev/sdh' => '7',
15
+ '/dev/sdi' => '8',
16
+ '/dev/sdj' => '9',
17
+ '/dev/xvdb' => '1',
18
+ '/dev/xvdc' => '2',
19
+ '/dev/xvdd' => '3',
20
+ '/dev/xvde' => '4',
21
+ '/dev/xvdf' => '5',
22
+ '/dev/xvdg' => '6',
23
+ '/dev/xvdh' => '7',
24
+ '/dev/xvdi' => '8',
25
+ '/dev/xvdj' => '9',
26
+ 'xvdb' => '1',
27
+ 'xvdc' => '2',
28
+ 'xvdd' => '3',
29
+ 'xvde' => '4',
30
+ 'xvdf' => '5',
31
+ 'xvdg' => '6',
32
+ 'xvdh' => '7',
33
+ 'xvdi' => '8',
34
+ 'xvdj' => '9',
35
+ }
36
+ #comes from awsapi reference
37
+ STRINGEXP = /.+/
38
+ INTEXP = /[0-9]+/
39
+ UUIDEXP = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
40
+
41
+ def get_resolved(lookup_data,workitem)
42
+ case lookup_data
43
+ when String
44
+ lookup_data
45
+ when Hash
46
+ intrinsic(lookup_data,workitem)
47
+ end
48
+ end
49
+
50
+ def validate_param(value,type)
51
+ return true
52
+ begin
53
+ case type
54
+ when "boolean"
55
+ ["true","false"].include?(value)
56
+ when "date"
57
+ true
58
+ when "imageformat"
59
+ ["vhd","qcow"].include?(value)
60
+ when "int"
61
+ !INTEXP.match(value).nil?
62
+ when "integer"
63
+ !INTEXP.match(value).nil?
64
+ when "list"
65
+ #eval(value).kind_of?(Array)
66
+ true
67
+ when "long"
68
+ !INTEXP.match(value).nil?
69
+ when "map"
70
+ #eval(value).kind_of?(Hash)
71
+ true
72
+ when "set"
73
+ #eval(value).kind_of?(Array) and eval(value).uniq == eval(value)
74
+ true
75
+ when "short"
76
+ !INTEXP.match(value).nil?
77
+ when "state"
78
+ true
79
+ when "string"
80
+ value.kind_of?(String)
81
+ when "tzdate"
82
+ true
83
+ when "uuid"
84
+ !UUIDEXP.match(value).nil?
85
+ else
86
+ true
87
+ end
88
+ rescue Exception => e
89
+ false
90
+ end
91
+ end
92
+
93
+ def get_named_tag(tag_name,properties,workitem,default)
94
+ result = default
95
+ unless properties['Tags'].nil?
96
+ properties['Tags'].each do |tag|
97
+ k = tag['Key']
98
+ v = tag['Value']
99
+ if k.eql?(tag_name)
100
+ result = get_resolved(v,workitem)
101
+ end
102
+ end
103
+ end
104
+ result
105
+ end
106
+
107
+ def resolve_tags(tags_array,workitem)
108
+ result = {}
109
+ tags_array.each do |tag|
110
+ k = tag['key']
111
+ v = tag['value']
112
+ resolved_v = get_resolved(v,workitem)
113
+ result[k] = resolved_v
114
+ end
115
+ result
116
+ end
117
+
118
+ def resolve_to_deviceid(devicename)
119
+ @devicename_map[devicename.downcase]
120
+ end
121
+ end
122
+ end
@@ -2,92 +2,148 @@ require 'json'
2
2
  require 'set'
3
3
  require 'tsort'
4
4
  require 'stackmate/logging'
5
+ require 'yaml'
5
6
 
6
7
  module StackMate
7
8
 
8
- class Stacker
9
+ class Stacker
9
10
  include TSort
10
11
  include Logging
11
12
 
12
13
  attr_accessor :templ
13
14
 
14
15
  def initialize(stackstr, stackname, params)
15
- @stackname = stackname
16
- @resolved = params
17
- @templ = JSON.parse(stackstr)
18
- @templ['StackName'] = @stackname
19
- @param_names = @templ['Parameters']
20
- @deps = {}
21
- @pdeps = {}
22
- validate_param_values
23
- resolve_dependencies()
24
- @templ['ResolvedNames'] = @resolved
16
+ @stackname = stackname
17
+ @resolved = params
18
+ @templ = JSON.parse(stackstr)
19
+ @templ['StackName'] = @stackname
20
+ @param_names = @templ['Parameters']
21
+ @deps = {}
22
+ @pdeps = {}
23
+ validate_param_values
24
+ resolve_dependencies()
25
+ validate_dependencies
26
+ @allowed_param_vales = get_allowed_values(@param_names)
27
+ @templ['ResolvedNames'] = populate_parameters(@param_names,@resolved)
28
+ #@templ['ResolvedNames']['StackId'] = SecureRandom.urlsafe_base64
29
+ @templ['IdMap'] = {}
30
+ end
31
+
32
+ def get_allowed_values(template_params)
33
+ allowed = {}
34
+ template_params.each_key do |k|
35
+ #Type is mandatory???
36
+ allowed[k] = {}
37
+ allowed[k]['Type'] = template_params[k]['Type'] if template_params[k].has_key?('Type')
38
+ allowed[k]['AllowedValues'] = template_params[k]['AllowedValues'] if template_params[k].has_key?('AllowedValues')
39
+ end
40
+ allowed
41
+ end
42
+
43
+ def populate_parameters(params,overrides)
44
+ populated={}
45
+ #Populate defaults
46
+ params.each_key do |k|
47
+ populated[k] = params[k]['Default']
48
+ end
49
+ #Then load local YAML mappings
50
+ begin
51
+ #TODO change to use stackid
52
+ #file_name = @stackname+".yml"
53
+ file_name = "local.yml"
54
+ localized = YAML.load_file(file_name)
55
+ localized.each_key do |k|
56
+ populated[k] = localized[k]
57
+ end
58
+ rescue
59
+ #raise "ERROR : Unable to load end point parameters"
60
+ logger.info("CAUTION: Unable to load end point parameters")
61
+ end
62
+ #Then override
63
+ overrides.each_key do |k|
64
+ populated[k] = overrides[k]
65
+ end
66
+ populated
25
67
  end
26
68
 
27
-
28
69
  def validate_param_values
29
- #TODO CloudFormation parameters have validity constraints specified
30
- #Use them to validate parameter values (e.g., email addresses)
70
+ #TODO CloudFormation parameters have validity constraints specified
71
+ #Use them to validate parameter values (e.g., email addresses)
72
+ #As of now used only in actual cloudstack calls
73
+ end
74
+
75
+ def validate_dependencies
76
+ resources = @deps.keys
77
+ @deps.each_key do |k|
78
+ @deps[k].each do |resource|
79
+ if !resources.include?(resource)
80
+ raise "Bad reference or dependency on resource #{resource}"
81
+ end
82
+ end
83
+ end
31
84
  end
32
85
 
33
86
  def resolve_dependencies
34
- @templ['Resources'].each { |key,val|
35
- deps = Set.new
36
- pdeps = Set.new
37
- find_refs(key, val, deps, pdeps)
38
- deps << val['DependsOn'] if val['DependsOn']
39
- #print key, " depends on ", deps.to_a, "\n"
40
- @deps[key] = deps.to_a
41
- @pdeps[key] = pdeps.to_a
42
- }
43
- @pdeps.keys.each do |k|
44
- unres = @pdeps[k] - @resolved.keys
45
- if ! unres.empty?
46
- unres.each do |u|
47
- deflt = @param_names[u]['Default']
48
- #print "Found default value ", deflt, " for ", u, "\n" if deflt
49
- @resolved[u] = deflt if deflt
50
- end
51
- unres = @pdeps[k] - @resolved.keys
52
- throw :unresolved, (@pdeps[k] - @resolved.keys) if !unres.empty?
53
- end
87
+ @templ['Resources'].each { |key,val|
88
+ deps = Set.new
89
+ pdeps = Set.new
90
+ find_refs(key, val, deps, pdeps)
91
+ deps << val['DependsOn'] if val['DependsOn']
92
+ #print key, " depends on ", deps.to_a, "\n"
93
+ @deps[key] = deps.to_a
94
+ @pdeps[key] = pdeps.to_a
95
+ }
96
+ unresolved = []
97
+ @pdeps.keys.each do |k|
98
+ unres = @pdeps[k] - @resolved.keys
99
+ if ! unres.empty?
100
+ unres.each do |u|
101
+ deflt = @param_names[u]['Default']
102
+ #print "Found default value ", deflt, " for ", u, "\n" if deflt
103
+ @resolved[u] = deflt if deflt
104
+ end
105
+ unres = @pdeps[k] - @resolved.keys
106
+ unresolved = unresolved + unres
107
+ #throw :unresolved, (@pdeps[k] - @resolved.keys) if !unres.empty?
54
108
  end
109
+ end
110
+ throw :unresolved, unresolved.uniq if !unresolved.empty?
55
111
  end
56
112
 
57
113
 
58
114
  def find_refs (parent, jsn, deps, pdeps)
59
- case jsn
60
- when Array
61
- jsn.each {|x| find_refs(parent, x, deps, pdeps)}
62
- #print parent, ": ", jsn, "\n"
63
- when Hash
64
- jsn.keys.each do |k|
65
- #TODO Fn::GetAtt
66
- if k == "Ref"
67
- #only resolve dependencies on other resources for now
68
- if !@param_names.keys.index(jsn[k]) && jsn[k] != 'AWS::Region' && jsn[k] != 'AWS::StackId' && jsn[k] != 'AWS::StackName'
69
- deps << jsn[k]
70
- #print parent, ": ", deps.to_a, "\n"
71
- else if @param_names.keys.index(jsn[k])
72
- pdeps << jsn[k]
73
- end
74
- end
75
- else
76
- find_refs(parent, jsn[k], deps, pdeps)
77
- end
78
- end
115
+ case jsn
116
+ when Array
117
+ jsn.each {|x| find_refs(parent, x, deps, pdeps)}
118
+ #print parent, ": ", jsn, "\n"
119
+ when Hash
120
+ jsn.keys.each do |k|
121
+ #TODO Fn::GetAtt
122
+ if k == "Ref"
123
+ #only resolve dependencies on other resources for now
124
+ if !@param_names.keys.index(jsn[k]) && !@resolved.keys.index(jsn[k]) && jsn[k] != 'AWS::Region' && jsn[k] != 'AWS::StackId' && jsn[k] != 'AWS::StackName'
125
+ deps << jsn[k]
126
+ #print parent, ": ", deps.to_a, "\n"
127
+ else if @param_names.keys.index(jsn[k])
128
+ pdeps << jsn[k]
129
+ end
130
+ end
131
+ else
132
+ find_refs(parent, jsn[k], deps, pdeps)
133
+ end
79
134
  end
80
- return deps
135
+ end
136
+ return deps
81
137
  end
82
138
 
83
139
  def tsort_each_node(&block)
84
- @deps.each_key(&block)
140
+ @deps.each_key(&block)
85
141
  end
86
142
 
87
143
  def tsort_each_child(name, &block)
88
- @deps[name].each(&block) if @deps.has_key?(name)
144
+ @deps[name].each(&block) if @deps.has_key?(name)
89
145
  end
90
-
91
- end
92
146
 
93
- end
147
+ end
148
+
149
+ end