stackmate 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -64,9 +64,13 @@ $ export URL="http://localhost:8080/client/api"
64
64
  You need a couple of mappings from AWS ids to your CloudStack implementation:
65
65
 
66
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'}
67
+ $ cat local.yml
68
+ ---
69
+ service_offerings:
70
+ m1.small: 1c8db272-f95a-406c-bce3-39192ce965fa
71
+ templates:
72
+ ami-1b814f72: 3ea4563e-c7eb-11e2-b0ed-7f3baba63e45
73
+ zoneid: b3409835-02b0-4d21-bba4-1f659402117e
70
74
  ```
71
75
 
72
76
  * Ensure you have a ssh keypair called 'Foo' (used in the template parameter below) for your account FIRST:
data/bin/stackmate ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ bundle exec ruby -Ilib $(dirname $0)/stackmate.rb $*
data/bin/stackmate.rb ADDED
@@ -0,0 +1,74 @@
1
+ require 'ruote'
2
+ require 'ruote/storage/hash_storage'
3
+ require 'optparse'
4
+ require 'stackmate'
5
+ require 'stackmate/classmap'
6
+ require 'stackmate/waitcondition_server'
7
+
8
+
9
+ options = {}
10
+ stack_name = ''
11
+ opt_parser = OptionParser.new do |opts|
12
+ opts.banner = "Usage: stackmate.rb STACK_NAME [options]"
13
+ opts.separator ""
14
+ opts.separator "Specific options:"
15
+ opts.on(:REQUIRED, "--template-file FILE", String, "Path to the file that contains the template") do |f|
16
+ options[:file] = f
17
+ end
18
+ opts.on("-p", "--parameters [KEY1=VALUE1 KEY2=VALUE2..]", "Parameter values used to create the stack.") do |p|
19
+ options[:params] = p
20
+ puts p
21
+ end
22
+ options[:wait_conditions] = true
23
+ opts.on("-n", "--no-wait-conditions", "Do not create any wait conditions") do
24
+ options[:wait_conditions] = false
25
+ end
26
+ options[:dry_run] = false
27
+ opts.on("-r", "--dry-run", "Parse and pretend to execute but not actually do anything. Useful for validating the template") do
28
+ options[:dry_run] = true
29
+ end
30
+ opts.on("-h", "--help", "Show this message") do
31
+ puts opts
32
+ exit
33
+ end
34
+ end
35
+
36
+ begin
37
+ opt_parser.parse!(ARGV)
38
+ if ARGV.size == 1
39
+ stack_name = ARGV[0]
40
+ end
41
+ rescue => e
42
+ puts e.message.capitalize
43
+ puts opt_parser.help()
44
+ exit 1
45
+ end
46
+
47
+ if options[:file] && stack_name != ''
48
+ if options[:wait_conditions]
49
+ Thread.new do
50
+ StackMate::WaitConditionServer.run!
51
+ end
52
+ end
53
+ engine = Ruote::Dashboard.new(
54
+ Ruote::Worker.new(
55
+ Ruote::HashStorage.new))
56
+ engine.noisy = ENV['NOISY'] == 'true'
57
+
58
+ unknown = nil
59
+ unresolved = catch(:unresolved) do
60
+ unknown = catch(:unknown) do
61
+ StackMate.configure('NOOP') if options[:dry_run]
62
+ opts = {}
63
+ api_opts = {:APIKEY => "#{ENV['APIKEY']}", :SECKEY => "#{ENV['SECKEY']}", :URL => "#{ENV['URL']}" }
64
+ p = StackMate::StackExecutor.new(options[:file], stack_name, options[:params], engine, options[:wait_conditions], api_opts)
65
+ p.launch()
66
+ nil
67
+ end
68
+ nil
69
+ end
70
+ puts 'Failed to resolve parameters ' + unresolved.to_s if unresolved
71
+ print "Sorry, I don't know how to create resources of type: ", unknown, "\n" if unknown
72
+ else
73
+ puts opt_parser.help()
74
+ end
@@ -16,10 +16,11 @@ class CloudStackResource < Ruote::Participant
16
16
 
17
17
  attr_reader :name
18
18
 
19
- def initialize()
20
- @url = ENV['URL']
21
- @apikey = ENV['APIKEY']
22
- @seckey = ENV['SECKEY']
19
+ def initialize(opts)
20
+ @opts = opts
21
+ @url = opts['URL'] || ENV['URL'] or raise ArgumentError.new("CloudStackResources: no URL supplied for CloudStack API")
22
+ @apikey = opts['APIKEY'] || ENV['APIKEY'] or raise ArgumentError.new("CloudStackResources: no api key supplied for CloudStack API")
23
+ @seckey = opts['SECKEY'] || ENV['SECKEY'] or raise ArgumentError.new("CloudStackResources: no secret key supplied for CloudStack API")
23
24
  @client = CloudstackRubyClient::Client.new(@url, @apikey, @seckey, false)
24
25
  end
25
26
 
@@ -65,17 +66,17 @@ class CloudStackResource < Ruote::Participant
65
66
  end
66
67
 
67
68
  class CloudStackInstance < CloudStackResource
68
- def initialize()
69
- super
69
+ def initialize(opts)
70
+ super (opts)
70
71
  @localized = {}
71
72
  load_local_mappings()
72
73
  end
73
74
 
74
75
  def on_workitem
75
- myname = workitem.participant_name
76
+ workitem[participant_name] = {}
77
+ myname = participant_name
76
78
  @name = myname
77
79
  resolved = workitem.fields['ResolvedNames']
78
- resolved['AWS::StackId'] = workitem.fei.wfid #TODO put this at launch time
79
80
  props = workitem.fields['Resources'][workitem.participant_name]['Properties']
80
81
  security_group_names = []
81
82
  props['SecurityGroups'].each do |sg|
@@ -103,6 +104,8 @@ class CloudStackInstance < CloudStackResource
103
104
  resultobj = make_request('deployVirtualMachine', args)
104
105
  logger.debug("Created resource #{myname}")
105
106
 
107
+ logger.debug("result = #{resultobj.inspect}")
108
+ workitem[participant_name][:physical_id] = resultobj['virtualmachine']['id']
106
109
  reply
107
110
  end
108
111
 
@@ -118,9 +121,9 @@ class CloudStackInstance < CloudStackResource
118
121
 
119
122
  def load_local_mappings()
120
123
  begin
121
- @localized = YAML.load_file('local.yaml')
124
+ @localized = YAML.load_file('local.yml')
122
125
  rescue
123
- print "Warning: Failed to load localized mappings from local.yaml\n"
126
+ logger.warning "Warning: Failed to load localized mappings from local.yaml\n"
124
127
  end
125
128
  end
126
129
 
@@ -166,6 +169,7 @@ end
166
169
  class CloudStackSecurityGroup < CloudStackResource
167
170
  def on_workitem
168
171
  myname = workitem.participant_name
172
+ workitem[participant_name] = {}
169
173
  logger.debug("Going to create resource #{myname}")
170
174
  @name = myname
171
175
  p myname
@@ -176,7 +180,7 @@ class CloudStackSecurityGroup < CloudStackResource
176
180
  args = { 'name' => name,
177
181
  'description' => props['GroupDescription']
178
182
  }
179
- make_request('createSecurityGroup', args)
183
+ sg_resp = make_request('createSecurityGroup', args)
180
184
  logger.debug("created resource #{myname}")
181
185
  props['SecurityGroupIngress'].each do |rule|
182
186
  cidrIp = rule['CidrIp']
@@ -194,6 +198,7 @@ class CloudStackSecurityGroup < CloudStackResource
194
198
  #TODO handle usersecuritygrouplist
195
199
  make_request('authorizeSecurityGroupIngress', args)
196
200
  end
201
+ workitem[participant_name][:physical_id] = sg_resp['securitygroup']['id']
197
202
  reply
198
203
  end
199
204
  end
@@ -7,13 +7,14 @@ class WaitConditionHandle < Ruote::Participant
7
7
  include Logging
8
8
 
9
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
10
+ logger.debug "Entering #{participant_name} "
11
+ workitem[participant_name] = {}
12
+ presigned_url = 'http://localhost:4567/waitcondition/' + workitem.fei.wfid + '/' + participant_name
13
+ workitem.fields['ResolvedNames'][participant_name] = presigned_url
14
14
  logger.info "Your pre-signed URL is: #{presigned_url} "
15
15
  logger.info "Try: \ncurl -X PUT --data 'foo' #{presigned_url}"
16
- WaitCondition.create_handle(myname, presigned_url)
16
+ WaitCondition.create_handle(participant_name, presigned_url)
17
+ workitem[participant_name][:physical_id] = presigned_url
17
18
 
18
19
  reply
19
20
  end
@@ -23,10 +24,13 @@ class WaitCondition < Ruote::Participant
23
24
  include Logging
24
25
  @@handles = {}
25
26
  @@conditions = []
27
+
26
28
  def on_workitem
27
29
  logger.debug "Entering #{workitem.participant_name} "
30
+ workitem[participant_name] = {}
28
31
  @@conditions << self
29
- @wi = workitem
32
+ stackname = workitem.fields['ResolvedNames']['AWS::StackName']
33
+ workitem[participant_name][:physical_id] = stackname + '-' + 'WaitCondition'
30
34
  end
31
35
 
32
36
  def self.create_handle(handle_name, handle)
@@ -34,7 +38,7 @@ class WaitCondition < Ruote::Participant
34
38
  end
35
39
 
36
40
  def set_handle(handle_name)
37
- reply(@wi) if @@handles[handle_name]
41
+ reply(workitem) if @@handles[handle_name]
38
42
  end
39
43
 
40
44
  def self.get_conditions()
@@ -56,7 +60,11 @@ class NoOpResource < Ruote::Participant
56
60
  include Logging
57
61
 
58
62
  def on_workitem
59
- logger.debug "Entering #{workitem.participant_name} "
63
+ logger.debug "Entering #{participant_name} wfid=#{workitem.fei.wfid} fei=#{workitem.fei.to_h}"
64
+ workitem[participant_name] = {}
65
+ stackname = workitem.fields['ResolvedNames']['AWS::StackName']
66
+ logger.debug "physical id is #{stackname}-#{participant_name} "
67
+ workitem[participant_name][:physical_id] = stackname + '-' + participant_name
60
68
  reply
61
69
  end
62
70
  end
@@ -9,28 +9,21 @@ class Stacker
9
9
  include TSort
10
10
  include Logging
11
11
 
12
- def initialize(templatefile, stackname, params)
12
+ attr_accessor :templ
13
+
14
+ def initialize(stackstr, stackname, params)
13
15
  @stackname = stackname
14
- @resolved = {}
15
- stackstr = File.read(templatefile)
16
+ @resolved = params
16
17
  @templ = JSON.parse(stackstr)
17
18
  @templ['StackName'] = @stackname
18
19
  @param_names = @templ['Parameters']
19
20
  @deps = {}
20
21
  @pdeps = {}
21
- resolve_param_refs(params)
22
22
  validate_param_values
23
23
  resolve_dependencies()
24
24
  @templ['ResolvedNames'] = @resolved
25
25
  end
26
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
27
 
35
28
  def validate_param_values
36
29
  #TODO CloudFormation parameters have validity constraints specified
@@ -72,7 +65,7 @@ class Stacker
72
65
  #TODO Fn::GetAtt
73
66
  if k == "Ref"
74
67
  #only resolve dependencies on other resources for now
75
- if !@param_names.keys.index(jsn[k]) && jsn[k] != 'AWS::Region' && jsn[k] != 'AWS::StackId'
68
+ if !@param_names.keys.index(jsn[k]) && jsn[k] != 'AWS::Region' && jsn[k] != 'AWS::StackId' && jsn[k] != 'AWS::StackName'
76
69
  deps << jsn[k]
77
70
  #print parent, ": ", deps.to_a, "\n"
78
71
  else if @param_names.keys.index(jsn[k])
@@ -11,10 +11,24 @@ module StackMate
11
11
  class StackExecutor < StackMate::Stacker
12
12
  include Logging
13
13
 
14
- def initialize(templatefile, stackname, params, engine, create_wait_conditions)
15
- super(templatefile, stackname, params)
14
+ def initialize(templatefile, stackname, params, engine, create_wait_conditions, api_opts)
15
+ stackstr = File.read(templatefile)
16
+ super(stackstr, stackname, resolve_param_refs(params, stackname))
16
17
  @engine = engine
17
18
  @create_wait_conditions = create_wait_conditions
19
+ @api_opts = api_opts
20
+ end
21
+
22
+ def resolve_param_refs(params, stackname)
23
+ resolved_params = {}
24
+ params.split(';').each do |p|
25
+ i = p.split('=')
26
+ resolved_params[i[0]] = i[1]
27
+ end
28
+ resolved_params['AWS::Region'] = 'us-east-1' #TODO handle this better
29
+ resolved_params['AWS::StackName'] = stackname
30
+ resolved_params['AWS::StackId'] = stackname
31
+ resolved_params
18
32
  end
19
33
 
20
34
  def pdef
@@ -29,13 +43,13 @@ class StackExecutor < StackMate::Stacker
29
43
  participants.each do |p|
30
44
  t = @templ['Resources'][p]['Type']
31
45
  throw :unknown, t if !StackMate.class_for(t)
32
- @engine.register_participant p, StackMate.class_for(t)
46
+ @engine.register_participant p, StackMate.class_for(t), @api_opts
33
47
  end
34
48
 
35
49
  @engine.register_participant 'Output', 'StackMate::Output'
36
50
  participants << 'Output'
37
51
  @pdef = Ruote.define @stackname.to_s() do
38
- cursor do
52
+ cursor :timeout => '300s' do
39
53
  participants.collect{ |name| __send__(name) }
40
54
  end
41
55
  end
@@ -1,3 +1,3 @@
1
1
  module StackMate
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/stackmate.gemspec CHANGED
@@ -11,9 +11,10 @@ Gem::Specification.new do |s|
11
11
  s.authors = ["Chiradeep Vittal"]
12
12
  s.email = 'chiradeepv@gmail.com'
13
13
  s.files = Dir[
14
- 'lib/**/*.rb', 'test/**/*.rb',
14
+ 'lib/**/*.rb', 'test/**/*.rb', 'bin/**',
15
15
  '*.gemspec', '*.txt', '*.rdoc', '*.md'
16
16
  ]
17
+ s.executables << 'stackmate'
17
18
  s.homepage =
18
19
  'https://github.com/chiradeep/stackmate'
19
20
  s.platform = Gem::Platform::RUBY
@@ -22,7 +23,7 @@ Gem::Specification.new do |s|
22
23
  s.add_runtime_dependency 'cloudstack_ruby_client', '>= 0.0.4'
23
24
  s.add_runtime_dependency 'json'
24
25
  s.add_runtime_dependency 'ruote', '>= 2.3.0'
25
- s.add_runtime_dependency 'sinatra', '>= 1.4.2'
26
+ s.add_runtime_dependency 'sinatra', '~> 1.3.6'
26
27
  s.add_runtime_dependency 'yajl-ruby', '= 1.1.0'
27
28
 
28
29
  s.add_development_dependency 'json'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stackmate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-16 00:00:00.000000000 Z
12
+ date: 2013-05-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: cloudstack_ruby_client
@@ -64,17 +64,17 @@ dependencies:
64
64
  requirement: !ruby/object:Gem::Requirement
65
65
  none: false
66
66
  requirements:
67
- - - ! '>='
67
+ - - ~>
68
68
  - !ruby/object:Gem::Version
69
- version: 1.4.2
69
+ version: 1.3.6
70
70
  type: :runtime
71
71
  prerelease: false
72
72
  version_requirements: !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
- - - ! '>='
75
+ - - ~>
76
76
  - !ruby/object:Gem::Version
77
- version: 1.4.2
77
+ version: 1.3.6
78
78
  - !ruby/object:Gem::Dependency
79
79
  name: yajl-ruby
80
80
  requirement: !ruby/object:Gem::Requirement
@@ -109,7 +109,8 @@ dependencies:
109
109
  version: '0'
110
110
  description: Parse and execute CloudFormation templates on CloudStack and other clouds
111
111
  email: chiradeepv@gmail.com
112
- executables: []
112
+ executables:
113
+ - stackmate
113
114
  extensions: []
114
115
  extra_rdoc_files: []
115
116
  files:
@@ -122,6 +123,8 @@ files:
122
123
  - lib/stackmate/version.rb
123
124
  - lib/stackmate/waitcondition_server.rb
124
125
  - lib/stackmate.rb
126
+ - bin/stackmate
127
+ - bin/stackmate.rb
125
128
  - stackmate.gemspec
126
129
  - LICENSE.txt
127
130
  - README.md