stackmate 0.0.1

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.
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Chiradeep Vittal
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ 'Software'), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,144 @@
1
+
2
+ # Stackmate - CloudFormation for CloudStack
3
+
4
+ A library designed to read existing CloudFormation templates
5
+ and execute them on a CloudStack deployment.
6
+ Uses the [ruote](http://ruote.rubyforge.org) workflow engine,
7
+ and embeds a modular [Sinatra](http://www.sinatrarb.com/) application for wait handles.
8
+
9
+ Unlike CloudFormation, it does not run as a web application.
10
+ Instead it runs everything on the client side.
11
+
12
+ [Stacktician](https://github.com/chiradeep/stacktician) embeds stackmate to run it as a web application.
13
+
14
+ Note that only Basic Zone (aka EC2-Classic) is supported for now.
15
+
16
+ Follow:
17
+ * \#cloudstack-dev on Freenode
18
+ * <http://cloudstack.apache.org/mailing-lists.html>
19
+ * [@chiradeep](http://twitter.com/chiradeep) on Twitter
20
+
21
+ ## Dependencies
22
+
23
+ stackmate uses [Bundler](http://gembundler.com/) to setup and maintain its
24
+ environment. Before running stackmate for the first time you need to install
25
+ Bundler (gem install bundler) and then run:
26
+
27
+ ```bash
28
+ $ bundle install
29
+
30
+ ```
31
+
32
+ Bundler will download all the required gems and install them for you.
33
+
34
+ Have a look at the Gemfile if you want to see the various dependencies.
35
+
36
+ ## Getting started quickly
37
+
38
+ ### Using the source
39
+
40
+ * Get the source files using git
41
+
42
+ ```bash
43
+ $ git clone http://github.com/chiradeep/stackmate.git
44
+ $ cd stackmate
45
+ ```
46
+
47
+ * Make sure every dependency is resolved.
48
+
49
+ ```bash
50
+ $ bundle install
51
+ ```
52
+ * Find your API key and secret key and the url for CloudStack
53
+
54
+ For example
55
+
56
+ ```bash
57
+ $ export APIKEY=upf7L-tvcHFCSYhKhw-45l9IfaKXNQSWf0nXyWye6eqOBpLT5TqN8XQGeuloV3LbSwD6zuucz22L233Nrqg2pg
58
+ $ export SECKEY=9iSsuImdUxU0oumHu0p11li4IoUtwcvrSHcU63ZHS_y-4Iz3w5xPROzyjZTUXkhI9E7dy0r3vejzgCmaQfI-yw
59
+ $ export URL="http://localhost:8080/client/api"
60
+ ```
61
+
62
+ * The supplied templates are taken from the AWS samples.
63
+
64
+ You need a couple of mappings from AWS ids to your CloudStack implementation:
65
+
66
+ ```bash
67
+ $ cat local.yaml
68
+ service_offerings : {'m1.small' : '13954c5a-60f5-4ec8-9858-f45b12f4b846'}
69
+ templates : {'ami-1b814f72': '7fc2c704-a950-11e2-8b38-0b06fbda5106'}
70
+ ```
71
+
72
+ * Ensure you have a ssh keypair called 'Foo' (used in the template parameter below) for your account FIRST:
73
+
74
+ ```bash
75
+ $ cloudmonkey
76
+ ☁ Apache CloudStack 🐵 cloudmonkey 4.1.0-snapshot3. Type help or ? to list commands.
77
+
78
+ > create sshkeypair name='Foo'
79
+ ```
80
+
81
+
82
+ * Create a LAMP stack:
83
+
84
+ ```bash
85
+ bin/stackmate MYSTACK01 --template-file=templates/LAMP_Single_Instance.template -p "DBName=cloud;DBUserName=cloud;SSHLocation=75.75.75.0/24;DBUsername=cloud;DBPassword=cloud;DBRootPassword=cloud;KeyName=Foo"
86
+ ```
87
+
88
+ * If everything is successful, stackmate will hang after deploying the security groups and vms.
89
+ You should see an output like this:
90
+
91
+ ```bash
92
+ Your pre-signed URL is: http://localhost:4567/waitcondition/20130425-0706-kerujere-punopapa/WaitHandle
93
+ Try: curl -X PUT --data 'foo' http://localhost:4567/waitcondition/20130425-0706-kerujere-punopapa/WaitHandle
94
+ ```
95
+ Executing the curl should unblock the wait handle. The idea of course is that the instance boots up, and reads its userdata and calls the same URL.
96
+
97
+ If you don't want the wait condition server to run, just use '-n'. Stackmate will not hang with this flag.
98
+
99
+ If you want to validate your template, you can use the --dry-run option. This will parse and validate the template and create an execution schedule.
100
+
101
+ ## TODO
102
+ * Parallelize (with ruote concurrence) where possible
103
+ * rollback on error
104
+ * timeouts
105
+ * embed in a web app ( [Stacktician](https://github.com/chiradeep/stacktician) )
106
+ * add support for Advanced Zone templates (VPC), LB, etc
107
+
108
+ ## Feedback & bug reports
109
+
110
+ Feedback and bug reports are welcome on the [mailing-list](dev@cloudstack.apache.org), or on the `#cloudstack-dev` IRC channel at Freenode.net.
111
+
112
+ ## License
113
+
114
+ (The MIT License)
115
+
116
+ Copyright (c) 2013 Chiradeep Vittal
117
+
118
+ Permission is hereby granted, free of charge, to any person obtaining
119
+ a copy of this software and associated documentation files (the
120
+ 'Software'), to deal in the Software without restriction, including
121
+ without limitation the rights to use, copy, modify, merge, publish,
122
+ distribute, sublicense, and/or sell copies of the Software, and to
123
+ permit persons to whom the Software is furnished to do so, subject to
124
+ the following conditions:
125
+
126
+ The above copyright notice and this permission notice shall be
127
+ included in all copies or substantial portions of the Software.
128
+
129
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
130
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
131
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
132
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
133
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
134
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
135
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
136
+
137
+ ## libraries used
138
+
139
+ - ruote, <http://ruote.rubyforge.org/>
140
+ - sinatra, <http://www.sinatrarb.com/>
141
+ - cloudstack_ruby_client, <https://github.com/chipchilders/cloudstack_ruby_client>
142
+
143
+ Many thanks to the authors
144
+
@@ -0,0 +1,25 @@
1
+ module StackMate
2
+ PROFILES = ['CLOUDSTACK', 'NOOP']
3
+ @profile = 'CLOUDSTACK'
4
+
5
+ CS_CLASS_MAP = {
6
+ 'AWS::CloudFormation::WaitConditionHandle' => 'StackMate::WaitConditionHandle',
7
+ 'AWS::CloudFormation::WaitCondition' => 'StackMate::WaitCondition',
8
+ 'AWS::EC2::Instance' => 'StackMate::CloudStackInstance',
9
+ 'AWS::EC2::SecurityGroup' => 'StackMate::CloudStackSecurityGroup'
10
+ }
11
+
12
+ def StackMate.class_for(cf_resource)
13
+ case @profile
14
+ when 'CLOUDSTACK'
15
+ return CS_CLASS_MAP[cf_resource]
16
+ when 'NOOP'
17
+ return 'StackMate::NoOpResource'
18
+ end
19
+ end
20
+
21
+ def StackMate.configure(profile)
22
+ @profile = profile
23
+ end
24
+
25
+ end
@@ -0,0 +1,28 @@
1
+ require 'logger'
2
+
3
+ module StackMate
4
+ module Logging
5
+ def logger
6
+ @logger ||= Logging.logger_for(self.class.name)
7
+ end
8
+
9
+ # Use a hash class-ivar to cache a unique Logger per class:
10
+ @loggers = {}
11
+
12
+ class << self
13
+ def logger_for(classname)
14
+ @loggers[classname] ||= configure_logger_for(classname)
15
+ end
16
+
17
+ def configure_logger_for(classname)
18
+ logger = Logger.new(STDOUT)
19
+ logger.progname = classname
20
+ logger.datetime_format= '%F %T'
21
+ logger.formatter = proc do |severity, datetime, progname, msg|
22
+ "[#{datetime}] #{severity} #{progname} #{msg}\n"
23
+ end
24
+ logger
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,202 @@
1
+ require 'json'
2
+ require 'cloudstack_ruby_client'
3
+ require 'yaml'
4
+ require 'stackmate/logging'
5
+
6
+ module StackMate
7
+
8
+ class CloudStackApiException < StandardError
9
+ def initialize(msg)
10
+ super(msg)
11
+ end
12
+ end
13
+
14
+ class CloudStackResource < Ruote::Participant
15
+ include Logging
16
+
17
+ attr_reader :name
18
+
19
+ def initialize()
20
+ @url = ENV['URL']
21
+ @apikey = ENV['APIKEY']
22
+ @seckey = ENV['SECKEY']
23
+ @client = CloudstackRubyClient::Client.new(@url, @apikey, @seckey, false)
24
+ end
25
+
26
+ def on_workitem
27
+ p workitem.participant_name
28
+ reply
29
+ end
30
+
31
+ protected
32
+
33
+ def make_request(cmd, args)
34
+ begin
35
+ logger.debug "Going to make request #{cmd} to CloudStack server for resource #{@name}"
36
+ resp = @client.send(cmd, args)
37
+ jobid = resp['jobid'] if resp
38
+ resp = api_poll(jobid, 3, 3) if jobid
39
+ return resp
40
+ rescue => e
41
+ logger.error("Failed to make request #{cmd} to CloudStack server while creating resource #{@name}")
42
+ logger.error e.message + "\n " + e.backtrace.join("\n ")
43
+ raise e
44
+ rescue SystemExit
45
+ logger.error "Rescued a SystemExit exception"
46
+ raise CloudStackApiException, "Did not get 200 OK while making api call #{cmd}"
47
+ end
48
+ end
49
+
50
+ def api_poll (jobid, num, period)
51
+ i = 0
52
+ loop do
53
+ break if i > num
54
+ resp = @client.queryAsyncJobResult({'jobid' => jobid})
55
+ if resp
56
+ return resp['jobresult'] if resp['jobstatus'] == 1
57
+ return {'jobresult' => {}} if resp['jobstatus'] == 2
58
+ end
59
+ sleep(period)
60
+ i += 1
61
+ end
62
+ return {}
63
+ end
64
+
65
+ end
66
+
67
+ class CloudStackInstance < CloudStackResource
68
+ def initialize()
69
+ super
70
+ @localized = {}
71
+ load_local_mappings()
72
+ end
73
+
74
+ def on_workitem
75
+ myname = workitem.participant_name
76
+ @name = myname
77
+ resolved = workitem.fields['ResolvedNames']
78
+ resolved['AWS::StackId'] = workitem.fei.wfid #TODO put this at launch time
79
+ props = workitem.fields['Resources'][workitem.participant_name]['Properties']
80
+ security_group_names = []
81
+ props['SecurityGroups'].each do |sg|
82
+ sg_name = resolved[sg['Ref']]
83
+ security_group_names << sg_name
84
+ end
85
+ keypair = resolved[props['KeyName']['Ref']] if props['KeyName']
86
+ userdata = nil
87
+ if props['UserData']
88
+ userdata = user_data(props['UserData'], resolved)
89
+ end
90
+ templateid = image_id(props['ImageId'], resolved, workitem.fields['Mappings'])
91
+ templateid = @localized['templates'][templateid] if @localized['templates']
92
+ svc_offer = resolved[props['InstanceType']['Ref']] #TODO fragile
93
+ svc_offer = @localized['service_offerings'][svc_offer] if @localized['service_offerings']
94
+ args = { 'serviceofferingid' => svc_offer,
95
+ 'templateid' => templateid,
96
+ 'zoneid' => default_zone_id,
97
+ 'securitygroupnames' => security_group_names.join(','),
98
+ 'displayname' => myname,
99
+ #'name' => myname
100
+ }
101
+ args['keypair'] = keypair if keypair
102
+ args['userdata'] = userdata if userdata
103
+ resultobj = make_request('deployVirtualMachine', args)
104
+ logger.debug("Created resource #{myname}")
105
+
106
+ reply
107
+ end
108
+
109
+ def user_data(datum, resolved)
110
+ #TODO make this more general purpose
111
+ actual = datum['Fn::Base64']['Fn::Join']
112
+ delim = actual[0]
113
+ data = actual[1].map { |d|
114
+ d.kind_of?(Hash) ? resolved[d['Ref']]: d
115
+ }
116
+ Base64.urlsafe_encode64(data.join(delim))
117
+ end
118
+
119
+ def load_local_mappings()
120
+ begin
121
+ @localized = YAML.load_file('local.yaml')
122
+ rescue
123
+ print "Warning: Failed to load localized mappings from local.yaml\n"
124
+ end
125
+ end
126
+
127
+ def default_zone_id
128
+ if @localized['zoneid']
129
+ @localized['zoneid']
130
+ else
131
+ '1'
132
+ end
133
+ end
134
+
135
+ def image_id(imgstring, resolved, mappings)
136
+ #TODO convoluted logic only handles the cases
137
+ #ImageId : {"Ref" : "FooBar"}
138
+ #ImageId : { "Fn::FindInMap" : [ "Map1", { "Ref" : "OuterKey" },
139
+ # { "Fn::FindInMap" : [ "Map2", { "Ref" : "InnerKey" }, "InnerVal" ] } ] },
140
+ #ImageId : { "Fn::FindInMap" : [ "Map1", { "Ref" : "Key" }, "Value" ] } ] },
141
+ if imgstring['Ref']
142
+ return resolved[imgstring['Ref']]
143
+ else
144
+ if imgstring['Fn::FindInMap']
145
+ key = resolved[imgstring['Fn::FindInMap'][1]['Ref']]
146
+ #print "Key = ", key, "\n"
147
+ if imgstring['Fn::FindInMap'][2]['Ref']
148
+ val = resolved[imgstring['Fn::FindInMap'][2]['Ref']]
149
+ #print "Val [Ref] = ", val, "\n"
150
+ else
151
+ if imgstring['Fn::FindInMap'][2]['Fn::FindInMap']
152
+ val = image_id(imgstring['Fn::FindInMap'][2], resolved, mappings)
153
+ #print "Val [FindInMap] = ", val, "\n"
154
+ else
155
+ val = imgstring['Fn::FindInMap'][2]
156
+ end
157
+ end
158
+ end
159
+ return mappings[imgstring['Fn::FindInMap'][0]][key][val]
160
+ end
161
+ end
162
+
163
+ end
164
+
165
+
166
+ class CloudStackSecurityGroup < CloudStackResource
167
+ def on_workitem
168
+ myname = workitem.participant_name
169
+ logger.debug("Going to create resource #{myname}")
170
+ @name = myname
171
+ p myname
172
+ resolved = workitem.fields['ResolvedNames']
173
+ props = workitem.fields['Resources'][myname]['Properties']
174
+ name = workitem.fields['StackName'] + '-' + workitem.participant_name;
175
+ resolved[myname] = name
176
+ args = { 'name' => name,
177
+ 'description' => props['GroupDescription']
178
+ }
179
+ make_request('createSecurityGroup', args)
180
+ logger.debug("created resource #{myname}")
181
+ props['SecurityGroupIngress'].each do |rule|
182
+ cidrIp = rule['CidrIp']
183
+ if cidrIp.kind_of? Hash
184
+ #TODO: some sort of validation
185
+ cidrIpName = cidrIp['Ref']
186
+ cidrIp = resolved[cidrIpName]
187
+ end
188
+ args = { 'securitygroupname' => name,
189
+ 'startport' => rule['FromPort'],
190
+ 'endport' => rule['ToPort'],
191
+ 'protocol' => rule['IpProtocol'],
192
+ 'cidrlist' => cidrIp
193
+ }
194
+ #TODO handle usersecuritygrouplist
195
+ make_request('authorizeSecurityGroupIngress', args)
196
+ end
197
+ reply
198
+ end
199
+ end
200
+
201
+
202
+ end
@@ -0,0 +1,64 @@
1
+ require 'stackmate/logging'
2
+
3
+ module StackMate
4
+
5
+
6
+ class WaitConditionHandle < Ruote::Participant
7
+ include Logging
8
+
9
+ def on_workitem
10
+ myname = workitem.participant_name
11
+ logger.debug "Entering #{workitem.participant_name} "
12
+ presigned_url = 'http://localhost:4567/waitcondition/' + workitem.fei.wfid + '/' + myname
13
+ workitem.fields['ResolvedNames'][myname] = presigned_url
14
+ logger.info "Your pre-signed URL is: #{presigned_url} "
15
+ logger.info "Try: \ncurl -X PUT --data 'foo' #{presigned_url}"
16
+ WaitCondition.create_handle(myname, presigned_url)
17
+
18
+ reply
19
+ end
20
+ end
21
+
22
+ class WaitCondition < Ruote::Participant
23
+ include Logging
24
+ @@handles = {}
25
+ @@conditions = []
26
+ def on_workitem
27
+ logger.debug "Entering #{workitem.participant_name} "
28
+ @@conditions << self
29
+ @wi = workitem
30
+ end
31
+
32
+ def self.create_handle(handle_name, handle)
33
+ @@handles[handle_name] = handle
34
+ end
35
+
36
+ def set_handle(handle_name)
37
+ reply(@wi) if @@handles[handle_name]
38
+ end
39
+
40
+ def self.get_conditions()
41
+ @@conditions
42
+ end
43
+ end
44
+
45
+ class Output < Ruote::Participant
46
+ include Logging
47
+ def on_workitem
48
+ #p workitem.fields.keys
49
+ logger.debug "Entering #{workitem.participant_name} "
50
+ logger.debug "Done"
51
+ reply
52
+ end
53
+ end
54
+
55
+ class NoOpResource < Ruote::Participant
56
+ include Logging
57
+
58
+ def on_workitem
59
+ logger.debug "Entering #{workitem.participant_name} "
60
+ reply
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,100 @@
1
+ require 'json'
2
+ require 'set'
3
+ require 'tsort'
4
+ require 'stackmate/logging'
5
+
6
+ module StackMate
7
+
8
+ class Stacker
9
+ include TSort
10
+ include Logging
11
+
12
+ def initialize(templatefile, stackname, params)
13
+ @stackname = stackname
14
+ @resolved = {}
15
+ stackstr = File.read(templatefile)
16
+ @templ = JSON.parse(stackstr)
17
+ @templ['StackName'] = @stackname
18
+ @param_names = @templ['Parameters']
19
+ @deps = {}
20
+ @pdeps = {}
21
+ resolve_param_refs(params)
22
+ validate_param_values
23
+ resolve_dependencies()
24
+ @templ['ResolvedNames'] = @resolved
25
+ end
26
+
27
+ def resolve_param_refs(params)
28
+ params.split(';').each do |p|
29
+ i = p.split('=')
30
+ @resolved[i[0]] = i[1]
31
+ end
32
+ @resolved['AWS::Region'] = 'us-east-1' #TODO handle this better
33
+ end
34
+
35
+ def validate_param_values
36
+ #TODO CloudFormation parameters have validity constraints specified
37
+ #Use them to validate parameter values (e.g., email addresses)
38
+ end
39
+
40
+ def resolve_dependencies
41
+ @templ['Resources'].each { |key,val|
42
+ deps = Set.new
43
+ pdeps = Set.new
44
+ find_refs(key, val, deps, pdeps)
45
+ deps << val['DependsOn'] if val['DependsOn']
46
+ #print key, " depends on ", deps.to_a, "\n"
47
+ @deps[key] = deps.to_a
48
+ @pdeps[key] = pdeps.to_a
49
+ }
50
+ @pdeps.keys.each do |k|
51
+ unres = @pdeps[k] - @resolved.keys
52
+ if ! unres.empty?
53
+ unres.each do |u|
54
+ deflt = @param_names[u]['Default']
55
+ #print "Found default value ", deflt, " for ", u, "\n" if deflt
56
+ @resolved[u] = deflt if deflt
57
+ end
58
+ unres = @pdeps[k] - @resolved.keys
59
+ throw :unresolved, (@pdeps[k] - @resolved.keys) if !unres.empty?
60
+ end
61
+ end
62
+ end
63
+
64
+
65
+ def find_refs (parent, jsn, deps, pdeps)
66
+ case jsn
67
+ when Array
68
+ jsn.each {|x| find_refs(parent, x, deps, pdeps)}
69
+ #print parent, ": ", jsn, "\n"
70
+ when Hash
71
+ jsn.keys.each do |k|
72
+ #TODO Fn::GetAtt
73
+ if k == "Ref"
74
+ #only resolve dependencies on other resources for now
75
+ if !@param_names.keys.index(jsn[k]) && jsn[k] != 'AWS::Region' && jsn[k] != 'AWS::StackId'
76
+ deps << jsn[k]
77
+ #print parent, ": ", deps.to_a, "\n"
78
+ else if @param_names.keys.index(jsn[k])
79
+ pdeps << jsn[k]
80
+ end
81
+ end
82
+ else
83
+ find_refs(parent, jsn[k], deps, pdeps)
84
+ end
85
+ end
86
+ end
87
+ return deps
88
+ end
89
+
90
+ def tsort_each_node(&block)
91
+ @deps.each_key(&block)
92
+ end
93
+
94
+ def tsort_each_child(name, &block)
95
+ @deps[name].each(&block) if @deps.has_key?(name)
96
+ end
97
+
98
+ end
99
+
100
+ end
@@ -0,0 +1,51 @@
1
+ require 'ruote'
2
+ require 'json'
3
+ require 'stackmate/stack'
4
+ require 'stackmate/logging'
5
+ require 'stackmate/classmap'
6
+ require 'stackmate/participants/cloudstack'
7
+ require 'stackmate/participants/common'
8
+
9
+ module StackMate
10
+
11
+ class StackExecutor < StackMate::Stacker
12
+ include Logging
13
+
14
+ def initialize(templatefile, stackname, params, engine, create_wait_conditions)
15
+ super(templatefile, stackname, params)
16
+ @engine = engine
17
+ @create_wait_conditions = create_wait_conditions
18
+ end
19
+
20
+ def pdef
21
+ participants = self.strongly_connected_components.flatten
22
+ #if we want to skip creating wait conditions (useful for automated tests)
23
+ participants = participants.select { |p|
24
+ StackMate.class_for(@templ['Resources'][p]['Type']) != 'StackMate::WaitCondition'
25
+ } if !@create_wait_conditions
26
+
27
+ logger.info("Ordered list of participants: #{participants}")
28
+
29
+ participants.each do |p|
30
+ t = @templ['Resources'][p]['Type']
31
+ throw :unknown, t if !StackMate.class_for(t)
32
+ @engine.register_participant p, StackMate.class_for(t)
33
+ end
34
+
35
+ @engine.register_participant 'Output', 'StackMate::Output'
36
+ participants << 'Output'
37
+ @pdef = Ruote.define @stackname.to_s() do
38
+ cursor do
39
+ participants.collect{ |name| __send__(name) }
40
+ end
41
+ end
42
+ end
43
+
44
+ def launch
45
+ wfid = @engine.launch( pdef, @templ)
46
+ @engine.wait_for(wfid)
47
+ logger.error { "engine error : #{@engine.errors.first.message}"} if @engine.errors.first
48
+ end
49
+ end
50
+
51
+ end
@@ -0,0 +1,3 @@
1
+ module StackMate
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,32 @@
1
+ require 'rufus-json/automatic'
2
+ require 'ruote'
3
+ require 'ruote/storage/fs_storage'
4
+ require 'json'
5
+ require 'sinatra/base'
6
+ require 'stackmate/participants/common'
7
+
8
+ module StackMate
9
+
10
+ class WaitConditionServer < Sinatra::Base
11
+ set :static, false
12
+ set :run, true
13
+
14
+ def initialize()
15
+ super
16
+ end
17
+
18
+ put '/waitcondition/:wfeid/:waithandle' do
19
+ #print "Got PUT of " , params[:wfeid], ", name = ", params[:waithandle], "\n"
20
+ WaitCondition.get_conditions.each do |w|
21
+ w.set_handle(params[:waithandle].to_s)
22
+ end
23
+ 'success
24
+ '
25
+ end
26
+
27
+
28
+ run! if app_file == $0
29
+
30
+ end
31
+
32
+ end
data/lib/stackmate.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'stackmate/stack'
2
+ require 'stackmate/stack_executor'
3
+ require 'stackmate/participants/common.rb'
4
+ require 'stackmate/participants/cloudstack.rb'
data/stackmate.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'stackmate/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'stackmate'
8
+ s.version = StackMate::VERSION
9
+ s.summary = "Execute CloudFormation templates on CloudStack"
10
+ s.description = "Parse and execute CloudFormation templates on CloudStack and other clouds"
11
+ s.authors = ["Chiradeep Vittal"]
12
+ s.email = 'chiradeepv@gmail.com'
13
+ s.files = Dir[
14
+ 'lib/**/*.rb', 'test/**/*.rb',
15
+ '*.gemspec', '*.txt', '*.rdoc', '*.md'
16
+ ]
17
+ s.homepage =
18
+ 'https://github.com/chiradeep/stackmate'
19
+ s.platform = Gem::Platform::RUBY
20
+
21
+ #s.add_runtime_dependency 'ruby_parser', '~> 2.3'
22
+ s.add_runtime_dependency 'cloudstack_ruby_client', '>= 0.0.4'
23
+ s.add_runtime_dependency 'json'
24
+ s.add_runtime_dependency 'ruote', '>= 2.3.0'
25
+ s.add_runtime_dependency 'sinatra', '>= 1.4.2'
26
+ s.add_runtime_dependency 'yajl-ruby', '= 1.1.0'
27
+
28
+ s.add_development_dependency 'json'
29
+
30
+ s.require_path = 'lib'
31
+ end
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stackmate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Chiradeep Vittal
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cloudstack_ruby_client
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.0.4
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.0.4
30
+ - !ruby/object:Gem::Dependency
31
+ name: json
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: ruote
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 2.3.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 2.3.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: sinatra
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 1.4.2
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 1.4.2
78
+ - !ruby/object:Gem::Dependency
79
+ name: yajl-ruby
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - '='
84
+ - !ruby/object:Gem::Version
85
+ version: 1.1.0
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - '='
92
+ - !ruby/object:Gem::Version
93
+ version: 1.1.0
94
+ - !ruby/object:Gem::Dependency
95
+ name: json
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: Parse and execute CloudFormation templates on CloudStack and other clouds
111
+ email: chiradeepv@gmail.com
112
+ executables: []
113
+ extensions: []
114
+ extra_rdoc_files: []
115
+ files:
116
+ - lib/stackmate/classmap.rb
117
+ - lib/stackmate/logging.rb
118
+ - lib/stackmate/participants/cloudstack.rb
119
+ - lib/stackmate/participants/common.rb
120
+ - lib/stackmate/stack.rb
121
+ - lib/stackmate/stack_executor.rb
122
+ - lib/stackmate/version.rb
123
+ - lib/stackmate/waitcondition_server.rb
124
+ - lib/stackmate.rb
125
+ - stackmate.gemspec
126
+ - LICENSE.txt
127
+ - README.md
128
+ homepage: https://github.com/chiradeep/stackmate
129
+ licenses: []
130
+ post_install_message:
131
+ rdoc_options: []
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ none: false
136
+ requirements:
137
+ - - ! '>='
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ! '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirements: []
147
+ rubyforge_project:
148
+ rubygems_version: 1.8.25
149
+ signing_key:
150
+ specification_version: 3
151
+ summary: Execute CloudFormation templates on CloudStack
152
+ test_files: []