stackmate 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []