rouster 0.7 → 0.41

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -3
  3. data/README.md +7 -241
  4. data/Rakefile +18 -55
  5. data/Vagrantfile +8 -26
  6. data/lib/rouster.rb +183 -404
  7. data/lib/rouster/deltas.rb +118 -577
  8. data/lib/rouster/puppet.rb +34 -209
  9. data/lib/rouster/testing.rb +59 -366
  10. data/lib/rouster/tests.rb +19 -70
  11. data/path_helper.rb +7 -5
  12. data/rouster.gemspec +1 -3
  13. data/test/basic.rb +1 -4
  14. data/test/functional/deltas/test_get_groups.rb +2 -74
  15. data/test/functional/deltas/test_get_packages.rb +4 -86
  16. data/test/functional/deltas/test_get_ports.rb +1 -26
  17. data/test/functional/deltas/test_get_services.rb +4 -43
  18. data/test/functional/deltas/test_get_users.rb +2 -35
  19. data/test/functional/puppet/test_facter.rb +1 -41
  20. data/test/functional/puppet/test_get_puppet_star.rb +68 -0
  21. data/test/functional/test_caching.rb +1 -5
  22. data/test/functional/test_dirs.rb +0 -25
  23. data/test/functional/test_get.rb +6 -10
  24. data/test/functional/test_inspect.rb +1 -1
  25. data/test/functional/test_is_file.rb +1 -17
  26. data/test/functional/test_new.rb +22 -233
  27. data/test/functional/test_put.rb +11 -9
  28. data/test/functional/test_restart.rb +4 -1
  29. data/test/functional/test_run.rb +3 -2
  30. data/test/puppet/test_apply.rb +11 -13
  31. data/test/puppet/test_roles.rb +173 -0
  32. data/test/unit/test_new.rb +0 -88
  33. data/test/unit/test_parse_ls_string.rb +0 -67
  34. data/test/unit/testing/test_validate_file.rb +47 -39
  35. data/test/unit/testing/test_validate_package.rb +10 -36
  36. metadata +6 -46
  37. data/.reek +0 -63
  38. data/.travis.yml +0 -11
  39. data/Gemfile +0 -17
  40. data/Gemfile.lock +0 -102
  41. data/LICENSE +0 -9
  42. data/examples/aws.rb +0 -85
  43. data/examples/openstack.rb +0 -61
  44. data/examples/passthrough.rb +0 -71
  45. data/lib/rouster/vagrant.rb +0 -311
  46. data/plugins/aws.rb +0 -347
  47. data/plugins/openstack.rb +0 -136
  48. data/test/functional/deltas/test_get_crontab.rb +0 -161
  49. data/test/functional/deltas/test_get_os.rb +0 -68
  50. data/test/functional/test_is_in_file.rb +0 -40
  51. data/test/functional/test_passthroughs.rb +0 -94
  52. data/test/functional/test_validate_file.rb +0 -131
  53. data/test/unit/puppet/resources/puppet_run_with_failed_exec +0 -59
  54. data/test/unit/puppet/resources/puppet_run_with_successful_exec +0 -61
  55. data/test/unit/puppet/test_get_puppet_star.rb +0 -91
  56. data/test/unit/puppet/test_puppet_parsing.rb +0 -44
  57. data/test/unit/testing/resources/osx-launchd +0 -285
  58. data/test/unit/testing/resources/rhel-systemd +0 -46
  59. data/test/unit/testing/resources/rhel-systemv +0 -41
  60. data/test/unit/testing/resources/rhel-upstart +0 -20
  61. data/test/unit/testing/test_get_services.rb +0 -178
  62. data/test/unit/testing/test_validate_cron.rb +0 -78
  63. data/test/unit/testing/test_validate_port.rb +0 -103
@@ -1,61 +0,0 @@
1
- require sprintf('%s/../%s', File.dirname(File.expand_path(__FILE__)), 'path_helper')
2
-
3
- require 'rouster'
4
- require 'plugins/openstack' # brings in fog and some helpers
5
-
6
- ostack = Rouster.new(
7
- :name => 'ostack-testing',
8
- :sudo => false,
9
- :logfile => true,
10
- :passthrough => {
11
- :type => :openstack, # Indicate OpenStack provider
12
- :openstack_auth_url => 'http://hostname.acme.com:5000/v2.0/tokens', # OpenStack API endpoint
13
- :openstack_username => 'some_user', # OpenStack console username
14
- :openstack_tenant => 'tenant_id', # Tenant ID
15
- :user => 'ssh_user_id', # SSH login ID
16
- :keypair => 'openstack_key_name', # Name of ssh keypair in OpenStack
17
- :image_ref => 'c0340afb-577d-1234-87b2-aebdd6d1838f', # Image ID in OpenStack
18
- :flavor_ref => '547d9af5-096c-1234-98df-7d23162556b8', # Flavor ID in OpenStack
19
- :openstack_api_key => 'secret_openstack_key', # OpenStack console password
20
- :key => '/path/to/ssh_keys.pem', # SSH key filename
21
- },
22
- :sshtunnel => false,
23
- :verbosity => 1,
24
- )
25
-
26
- p "UP(): #{ostack.up}"
27
- p "STATUS(): #{ostack.status}"
28
-
29
- ostack_copy = Rouster.new(
30
- :name => 'ostack-copy',
31
- :sudo => false,
32
- :logfile => true,
33
- :passthrough => {
34
- :type => :openstack, # Indicate OpenStack provider
35
- :openstack_auth_url => 'http://hostname.acme.com:5000/v2.0/tokens', # OpenStack API endpoint
36
- :openstack_username => 'some_user', # OpenStack console username
37
- :openstack_tenant => 'tenant_id', # Tenant ID
38
- :user => 'ssh_user_id', # SSH login ID
39
- :keypair => 'openstack_key_name', # Name of ssh keypair in OpenStack
40
- :openstack_api_key => 'secret_openstack_key', # OpenStack console password
41
- :instance => ostack.ostack_get_instance_id, # ID of a running OpenStack instance.
42
- },
43
- :sshtunnel => false,
44
- :verbosity => 1,
45
- )
46
-
47
- [ ostack, ostack_copy ].each do |o|
48
- p "ostack_get_instance_id: #{o.ostack_get_instance_id}"
49
-
50
- p "status: #{o.status}"
51
-
52
- p "ostack_get_ip(): #{o.ostack_get_ip()}"
53
-
54
- p "run(uptime): #{o.run('uptime')}"
55
- p "get(/etc/hosts): #{o.get('/etc/hosts')}"
56
- p "put(/etc/hosts, /tmp): #{o.put('/etc/hosts', '/tmp')}"
57
-
58
- end
59
-
60
- p "DESTROY(): #{ostack.destroy}"
61
- exit
@@ -1,71 +0,0 @@
1
- require sprintf('%s/../%s', File.dirname(File.expand_path(__FILE__)), 'path_helper')
2
-
3
- require 'rouster'
4
- require 'rouster/puppet'
5
- require 'rouster/tests'
6
-
7
- verbosity = ENV['VERBOSE'].nil? ? 4 : 0
8
-
9
- # .inspect of this is blank for sshkey and status, looks ugly, but is ~accurate.. fix this?
10
- local = Rouster.new(
11
- :name => 'local',
12
- :sudo => false,
13
- :passthrough => { :type => :local },
14
- :verbosity => verbosity,
15
- )
16
-
17
- remote = Rouster.new(
18
- :name => 'remote',
19
- :sudo => false,
20
- :passthrough => {
21
- :type => :remote,
22
- :host => `hostname`.chomp, # yep, the remote is actually local.. perhaps the right :type would be 'ssh' vs 'shellout' instead..
23
- :user => ENV['USER'],
24
- :key => sprintf('%s/.ssh/id_dsa', ENV['HOME']),
25
- },
26
- :verbosity => verbosity,
27
- )
28
-
29
- sudo = Rouster.new(
30
- :name => 'sudo',
31
- :sudo => true,
32
- :passthrough => { :type => :local },
33
- :verbosity => verbosity,
34
- )
35
-
36
- vagrant = Rouster.new(
37
- :name => 'ppm',
38
- :sudo => true,
39
- :verbosity => verbosity,
40
- )
41
-
42
- workers = [ local, remote, vagrant ]
43
-
44
- workers = [vagrant]
45
-
46
- workers.each do |r|
47
- p r
48
-
49
- ## vagrant command testing
50
- r.up()
51
- r.suspend()
52
- #r.destroy()
53
- r.up()
54
-
55
- p r.status() # why is this giving us nil after initial call? want to blame caching, but not sure
56
-
57
- r.is_vagrant_running?()
58
- r.sandbox_available?()
59
-
60
- if r.sandbox_available?()
61
- r.sandbox_on()
62
- r.sandbox_off()
63
- r.sandbox_rollback()
64
- r.sandbox_commit()
65
- end
66
-
67
- p r.run('echo foo')
68
-
69
- end
70
-
71
- exit
@@ -1,311 +0,0 @@
1
- require sprintf('%s/../../%s', File.dirname(File.expand_path(__FILE__)), 'path_helper')
2
-
3
- ## Vagrant specific (and related) methods
4
-
5
- class Rouster
6
-
7
- ##
8
- # vagrant
9
- #
10
- # abstraction layer to call vagrant faces
11
- #
12
- # parameters
13
- # * <face> - vagrant face to call (include arguments)
14
- def vagrant(face, sleep_time=10)
15
- if self.is_passthrough?
16
- @logger.warn(sprintf('calling [vagrant %s] on a passthrough host is a noop', face))
17
- return nil
18
- end
19
-
20
- unless @vagrant_concurrency.eql?(true)
21
- # TODO don't (ab|re)use variables
22
- 0.upto(@retries) do |try|
23
- break if self.is_vagrant_running?().eql?(false)
24
-
25
- sleep sleep_time # TODO log a message?
26
- end
27
- end
28
-
29
- 0.upto(@retries) do |try| # TODO should really be doing this with 'retry', but i think this code is actually cleaner
30
- begin
31
- return self._run(sprintf('cd %s; vagrant %s', File.dirname(@vagrantfile), face))
32
- rescue
33
- @logger.error(sprintf('failed vagrant command[%s], attempt[%s/%s]', face, try, retries)) if self.retries > 0
34
- sleep sleep_time
35
- end
36
- end
37
-
38
- raise InternalError.new(sprintf('failed to execute [%s], exitcode[%s], output[%s]', face, self.exitcode, self.get_output()))
39
-
40
-
41
- end
42
-
43
- ##
44
- # up
45
- # runs `vagrant up <name>` from the Vagrantfile path
46
- # if :sshtunnel is passed to the object during instantiation, the tunnel is created here as well
47
- def up
48
- @logger.info('up()')
49
-
50
- # don't like putting this here, may be refactored
51
- if self.is_passthrough?
52
- if (self.passthrough[:type].equal?(:aws) or self.passthrough[:type].equal?(:raiden))
53
- self.aws_up()
54
- elsif (self.passthrough[:type].equal?(:openstack))
55
- self.ostack_up()
56
- else
57
- self.vagrant(sprintf('up %s', @name))
58
- end
59
- else
60
- self.vagrant(sprintf('up %s', @name))
61
- end
62
-
63
- @ssh_info = nil # in case the ssh-info has changed, a la destroy/rebuild
64
- self.connect_ssh_tunnel() if @sshtunnel
65
- end
66
-
67
- ##
68
- # halt
69
- # runs `vagrant halt <name>` from the Vagrantfile path
70
- def halt
71
- @logger.info('halt()')
72
- self.vagrant(sprintf('halt %s', @name))
73
- end
74
-
75
- ##
76
- # package -- though vagrant docs still refer to 'repackage'
77
- # runs `vagrant package <name> <provider>`
78
- def package(provider='virtualbox') # TODO get the provider as a first class citizen on the rouster object
79
- @logger.info(sprintf('package(%s)', provider))
80
- self.vagrant(sprintf('package %s %s', @name, provider))
81
- end
82
-
83
- ##
84
- # destroy
85
- # runs `vagrant destroy <name>` from the Vagrantfile path
86
- def destroy
87
- @logger.info('destroy()')
88
-
89
- # don't like putting this here, may be refactored
90
- if self.is_passthrough?
91
- if (self.passthrough[:type].equal?(:aws) or self.passthrough[:type].equal?(:raiden))
92
- self.aws_destroy()
93
- elsif self.is_passthrough? and self.passthrough[:type].equal?(:openstack)
94
- self.ostack_destroy()
95
- else
96
- raise InternalError.new(sprintf('failed to execute destroy(), unsupported passthrough type %s', self.passthrough[:type]))
97
- end
98
- else
99
- self.vagrant(sprintf('destroy -f %s', @name))
100
- end
101
-
102
- disconnect_ssh_tunnel
103
- end
104
-
105
- ##
106
- # status
107
- #
108
- # runs `vagrant status <name>` from the Vagrantfile path
109
- # parses the status and provider out of output, but only status is returned
110
- def status
111
- status = nil
112
-
113
- if @cache_timeout
114
- if @cache.has_key?(:status)
115
- if (Time.now.to_i - @cache[:status][:time]) < @cache_timeout
116
- @logger.debug(sprintf('using cached status[%s] from [%s]', @cache[:status][:status], @cache[:status][:time]))
117
- return @cache[:status][:status]
118
- end
119
- end
120
- end
121
-
122
- # don't like putting this here, may be refactored
123
- @logger.info('status()')
124
- if self.is_passthrough?
125
- if (self.passthrough[:type].equal?(:aws) or self.passthrough[:type].equal?(:raiden))
126
- status = self.aws_status()
127
- elsif self.passthrough[:type].equal?(:openstack)
128
- status = self.ostack_status()
129
- else
130
- raise InternalError.new(sprintf('failed to execute status(), unsupported passthrough type %s', self.passthrough[:type]))
131
- end
132
- else
133
- self.vagrant(sprintf('status %s', @name))
134
-
135
- # else case here (both for nil/non-matching output) is handled by non-0 exit code
136
- output = self.get_output()
137
- if output.nil?
138
- if self.is_passthrough?() and self.passthrough[:type].eql?(:local)
139
- status = 'running'
140
- else
141
- status = 'not-created'
142
- end
143
- elsif output.match(/^#{@name}\s*(.*\s?\w+)\s\((.+)\)$/)
144
- # vagrant 1.2+, $1 = status, $2 = provider
145
- status = $1
146
- elsif output.match(/^#{@name}\s+(.+)$/)
147
- # vagrant 1.2-, $1 = status
148
- status = $1
149
- end
150
- end
151
-
152
- if @cache_timeout
153
- @cache[:status] = Hash.new unless @cache[:status].class.eql?(Hash)
154
- @cache[:status][:time] = Time.now.to_i
155
- @cache[:status][:status] = status
156
- @logger.debug(sprintf('caching status[%s] at [%s]', @cache[:status][:status], @cache[:status][:time]))
157
- end
158
-
159
- return status
160
- end
161
-
162
- ##
163
- # reload
164
- #
165
- # runs `vagrant reload <name> [--no-provision]` from the Vagrantfile path
166
- # +no_provision+ Boolean whether or not to stop reprovisioning
167
- def reload(no_provision = true)
168
-
169
- if self.is_passthrough?
170
- @logger.warn('calling [vagrant reload] on a passthrough host is a noop')
171
- return nil
172
- end
173
-
174
- @logger.info('reload()')
175
- self.vagrant(sprintf('reload %s %s', @name, no_provision ? '--no-provision' : ''))
176
- end
177
-
178
- ##
179
- # suspend
180
- #
181
- # runs `vagrant suspend <name>` from the Vagrantfile path
182
- def suspend
183
- @logger.info('suspend()')
184
- self.vagrant(sprintf('suspend %s', @name))
185
- disconnect_ssh_tunnel() unless self.is_passthrough?()
186
- end
187
-
188
- ##
189
- # is_vagrant_running?()
190
- #
191
- # returns true|false if a vagrant process is running on the host machine
192
- #
193
- # meant to be used to prevent race-y conditions when interacting with VirtualBox (potentially others, haven't tested)
194
- def is_vagrant_running?
195
- res = false
196
-
197
- begin
198
- # TODO would like to get the 2 -v greps into a single call..
199
- raw = self._run("ps -ef | grep -v 'grep' | grep -v 'ssh' | grep '#{self.vagrantbinary}'")
200
- res = true
201
- rescue
202
- end
203
-
204
- @logger.debug(sprintf('is_vagrant_running?[%s]', res))
205
- res
206
- end
207
-
208
- ##
209
- # sandbox_available?
210
- #
211
- # returns true or false after attempting to find out if the sandbox
212
- # subcommand is available
213
- def sandbox_available?
214
- if self.is_passthrough?
215
- @logger.warn('sandbox* methods on a passthrough host is a noop')
216
- return nil
217
- end
218
-
219
- if @cache.has_key?(:sandbox_available?)
220
- @logger.debug(sprintf('using cached sandbox_available?[%s]', @cache[:sandbox_available?]))
221
- return @cache[:sandbox_available?]
222
- end
223
-
224
- @logger.info('sandbox_available()')
225
- begin
226
- # at some point, vagrant changed its behavior on exit code here, so rescuing
227
- self._run(sprintf('cd %s; vagrant', File.dirname(@vagrantfile))) # calling 'vagrant' without parameters to determine available faces
228
- rescue
229
- end
230
-
231
- sandbox_available = false
232
- if self.get_output().match(/^\s+sandbox$/)
233
- sandbox_available = true
234
- end
235
-
236
- @cache[:sandbox_available?] = sandbox_available
237
- @logger.debug(sprintf('caching sandbox_available?[%s]', @cache[:sandbox_available?]))
238
- @logger.error('sandbox support is not available, please install the "sahara" gem first, https://github.com/jedi4ever/sahara') unless sandbox_available
239
-
240
- return sandbox_available
241
- end
242
-
243
- ##
244
- # sandbox_on
245
- # runs `vagrant sandbox on` from the Vagrantfile path
246
- def sandbox_on
247
- if self.is_passthrough?
248
- @logger.warn('sandbox* methods on a passthrough host is a noop')
249
- return nil
250
- end
251
-
252
- if self.sandbox_available?
253
- return self.vagrant(sprintf('sandbox on %s', @name))
254
- else
255
- raise ExternalError.new('sandbox plugin not installed')
256
- end
257
- end
258
-
259
- ##
260
- # sandbox_off
261
- # runs `vagrant sandbox off` from the Vagrantfile path
262
- def sandbox_off
263
- if self.is_passthrough?
264
- @logger.warn('sandbox* methods on a passthrough host is a noop')
265
- return nil
266
- end
267
-
268
- if self.sandbox_available?
269
- return self.vagrant(sprintf('sandbox off %s', @name))
270
- else
271
- raise ExternalError.new('sandbox plugin not installed')
272
- end
273
- end
274
-
275
- ##
276
- # sandbox_rollback
277
- # runs `vagrant sandbox rollback` from the Vagrantfile path
278
- def sandbox_rollback
279
- if self.is_passthrough?
280
- @logger.warn('sandbox* methods on a passthrough host is a noop')
281
- return nil
282
- end
283
-
284
- if self.sandbox_available?
285
- self.disconnect_ssh_tunnel
286
- self.vagrant(sprintf('sandbox rollback %s', @name))
287
- self.connect_ssh_tunnel
288
- else
289
- raise ExternalError.new('sandbox plugin not installed')
290
- end
291
- end
292
-
293
- ##
294
- # sandbox_commit
295
- # runs `vagrant sandbox commit` from the Vagrantfile path
296
- def sandbox_commit
297
- if self.is_passthrough?
298
- @logger.warn('sandbox* methods on a passthrough host is a noop')
299
- return nil
300
- end
301
-
302
- if self.sandbox_available?
303
- self.disconnect_ssh_tunnel
304
- self.vagrant(sprintf('sandbox commit %s', @name))
305
- self.connect_ssh_tunnel
306
- else
307
- raise ExternalError.new('sandbox plugin not installed')
308
- end
309
- end
310
-
311
- end
data/plugins/aws.rb DELETED
@@ -1,347 +0,0 @@
1
- #!/usr/bin/ruby
2
- ## plugins/aws.rb - provide helper functions for Rouster objects running on AWS/EC2
3
-
4
- require sprintf('%s/../%s', File.dirname(File.expand_path(__FILE__)), 'path_helper')
5
-
6
- require 'fog'
7
- require 'uri'
8
-
9
- class Rouster
10
-
11
- attr_reader :ec2, :elb # expose AWS workers
12
- attr_reader :instance_data # the result of the runInstances request
13
-
14
- def aws_get_url(url)
15
- # convenience method to run curls from inside the VM
16
- self.run(sprintf('curl -s %s', url))
17
- end
18
-
19
- # TODO should this be 'aws_ip'?
20
- def aws_get_ip (method = :internal, type = :public)
21
- # allowed methods: :internal (check meta-data inside VM), :aws (ask API)
22
- # allowed types: :public, :private
23
- self.aws_describe_instance
24
-
25
- if method.equal?(:internal)
26
- key = type.equal?(:public) ? 'public-ipv4' : 'local-ipv4'
27
- murl = sprintf('http://169.254.169.254/latest/meta-data/%s', key)
28
- result = self.aws_get_url(murl)
29
- else
30
- key = type.equal?(:public) ? 'ipAddress' : 'privateIpAddress'
31
- result = @instance_data[key]
32
- end
33
-
34
- result
35
- end
36
-
37
- def aws_get_userdata
38
- murl = 'http://169.254.169.254/latest/user-data/'
39
- result = self.aws_get_url(murl)
40
-
41
- if result.match(/\S=\S/)
42
- # TODO should we really be doing this?
43
- userdata = Hash.new()
44
- result.split("\n").each do |line|
45
- if line.match(/^(.*?)=(.*)/)
46
- userdata[$1] = $2
47
- end
48
- end
49
- else
50
- userdata = result
51
- end
52
-
53
- userdata
54
- end
55
-
56
- # return a hash containing meta-data items
57
- def aws_get_metadata
58
- murl = 'http://169.254.169.254/latest/meta-data/'
59
- result = self.aws_get_url(murl)
60
- metadata = Hash.new()
61
-
62
- # TODO this isn't entirely right.. if the element ends in '/', it's actually another level of hash..
63
- result.split("\n").each do |element|
64
- metadata[element] = self.aws_get_url(sprintf('%s%s', murl, element))
65
- end
66
-
67
- metadata
68
- end
69
-
70
- def aws_get_hostname (method = :internal, type = :public)
71
- # allowed methods: :internal (check meta-data inside VM), :aws (ask API)
72
- # allowed types: :public, :private
73
- self.aws_describe_instance
74
-
75
- result = nil
76
-
77
- if method.equal?(:internal)
78
- key = type.equal?(:public) ? 'public-hostname' : 'local-hostname'
79
- murl = sprintf('http://169.254.169.254/latest/meta-data/%s', key)
80
- result = self.aws_get_url(murl)
81
- else
82
- key = type.equal?(:public) ? 'dnsName' : 'privateDnsName'
83
- result = @instance_data[key]
84
- end
85
-
86
- result
87
- end
88
-
89
- def aws_get_instance ()
90
- if ! @instance_data.nil? and @instance_data.has_key?('instanceId')
91
- return @instance_data['instanceId'] # we already know the id
92
- elsif @passthrough.has_key?(:instance)
93
- return @passthrough[:instance] # we know the id we want
94
- else
95
- @logger.debug(sprintf('unable to determine ami-id from instance_data[%s] or passthrough specification[%s]', @instance_data, @passthrough))
96
- return nil # we don't have an id yet, likely a up() call
97
- end
98
- end
99
-
100
- def aws_get_ami ()
101
- if ! @instance_data.nil? and @instance_data.has_key?('ami')
102
- return @instance_data['ami']
103
- else
104
- return @passthrough[:ami]
105
- end
106
- end
107
-
108
- def aws_up
109
- # wait for machine to transition to running state and become sshable (TODO maybe make the second half optional)
110
- self.aws_connect
111
-
112
- status = self.status()
113
-
114
- if status.eql?('running')
115
- self.connect_ssh_tunnel
116
- self.passthrough[:instance] = self.aws_get_instance
117
- return self.aws_get_instance
118
- end
119
-
120
- # TODO provide more context
121
- @logger.info(sprintf('calling RunInstances ami[%s], size[%s], keypair[%s]',
122
- self.passthrough[:ami],
123
- self.passthrough[:size],
124
- self.passthrough[:keypair]
125
- ))
126
-
127
- server = @ec2.run_instances(
128
- self.passthrough[:ami],
129
- self.passthrough[:min_count],
130
- self.passthrough[:max_count],
131
- {
132
- 'InstanceType' => self.passthrough[:size],
133
- 'KeyName' => self.passthrough[:keypair],
134
- 'SecurityGroup' => self.passthrough[:security_groups],
135
- 'UserData' => self.passthrough[:userdata],
136
- },
137
- )
138
-
139
- @instance_data = server.data[:body]['instancesSet'][0]
140
-
141
- # wait until the machine starts
142
- ceiling = 9
143
- sleep_time = 20
144
- status = nil
145
- 0.upto(ceiling) do |try|
146
- status = self.aws_status
147
-
148
- @logger.debug(sprintf('describeInstances[%s]: [%s] [#%s]', self.aws_get_instance, status, try))
149
-
150
- if status.eql?('running') or status.eql?('16')
151
- @logger.info(sprintf('[%s] transitioned to state[%s]', self.aws_get_instance, self.aws_status))
152
- break
153
- end
154
-
155
- sleep sleep_time
156
- end
157
-
158
- raise sprintf('instance[%s] did not transition to running state, stopped trying at[%s]', self.aws_get_instance, status) unless status.eql?('running') or status.eql?('16')
159
-
160
- # TODO don't be this hacky
161
- self.aws_describe_instance # the server.data response doesn't include public hostname/ip
162
- if @passthrough[:type].eql?(:aws)
163
- @passthrough[:host] = @instance_data['dnsName']
164
- else
165
- @passthrough[:host] = self.find_ssh_elb(true)
166
- end
167
-
168
- self.connect_ssh_tunnel
169
-
170
- self.passthrough[:instance] = self.aws_get_instance
171
- self.passthrough[:instance]
172
- end
173
-
174
- def aws_destroy
175
- self.aws_connect
176
-
177
- server = @ec2.terminate_instances(self.aws_get_instance)
178
-
179
- if self.passthrough[:created_elb] && self.passthrough[:elb_cleanup]
180
- @logger.info(sprintf('deleting ELB[%s]', self.passthrough[:created_elb]))
181
- @elb.delete_load_balancer(self.passthrough[:created_elb])
182
- end
183
-
184
- self.aws_status
185
- end
186
-
187
- def aws_describe_instance(instance = aws_get_instance)
188
-
189
- if @cache_timeout
190
- if @cache.has_key?(:aws_describe_instance)
191
- if (Time.now.to_i - @cache[:aws_describe_instance][:time]) < @cache_timeout
192
- @logger.debug(sprintf('using cached aws_describe_instance?[%s] from [%s]', @cache[:aws_describe_instance][:instance], @cache[:aws_describe_instance][:time]))
193
- return @cache[:aws_describe_instance][:instance]
194
- end
195
- end
196
- end
197
-
198
- return nil if instance.nil?
199
-
200
- self.aws_connect
201
- server = @ec2.describe_instances('instance-id' => [ instance ])
202
- response = server.data[:body]['reservationSet'][0]['instancesSet'][0]
203
-
204
- if instance.eql?(self.aws_get_instance)
205
- @instance_data = response
206
- end
207
-
208
- if @cache_timeout
209
- @cache[:aws_describe_instance] = Hash.new unless @cache[:aws_describe_instance].class.eql?(Hash)
210
- @cache[:aws_describe_instance][:time] = Time.now.to_i
211
- @cache[:aws_describe_instance][:instance] = response
212
- @logger.debug(sprintf('caching is_available_via_ssh?[%s] at [%s]', @cache[:aws_describe_instance][:instance], @cache[:aws_decribe_instance][:time]))
213
- end
214
-
215
- response
216
- end
217
-
218
- def aws_status
219
- self.aws_describe_instance
220
- return 'not-created' if @instance_data.nil?
221
- @instance_data['instanceState']['name'].nil? ? @instance_data['instanceState']['code'] : @instance_data['instanceState']['name']
222
- end
223
-
224
- def aws_connect_to_elb (id, elbname, listeners = [{ 'InstancePort' => 22, 'LoadBalancerPort' => 22, 'Protocol' => 'TCP' }])
225
- self.elb_connect
226
-
227
- # allow either hash or array of hash specification for listeners
228
- listeners = [ listeners ] unless listeners.is_a?(Array)
229
- required_params = [ 'InstancePort', 'LoadBalancerPort', 'Protocol' ] # figure out plan re: InstanceProtocol/LoadbalancerProtocol vs. Protocol
230
-
231
- listeners.each do |l|
232
- required_params.each do |r|
233
- raise sprintf('listener[%s] does not include required parameter[%s]', l, r) unless l[r]
234
- end
235
-
236
- end
237
-
238
- @logger.debug(sprintf('confirming ELB name uniqueness[%s]', elbname))
239
- response = @elb.describe_load_balancers()
240
- response.body['DescribeLoadBalancersResult']['LoadBalancerDescriptions'].each do |elb|
241
- if elb['LoadBalancerName'].eql?(elbname)
242
- # terminate
243
- @logger.debug(sprintf('terminating ELB[%s]', elbname))
244
- @elb.delete_load_balancer(elbname)
245
- end
246
- end
247
-
248
- # create the ELB/VIP
249
- @logger.debug(sprintf('creating a load balancer[%s] with listeners[%s]', elbname, listeners))
250
- response = @elb.create_load_balancer(
251
- [], # availability zones not needed on raiden
252
- elbname,
253
- listeners
254
- )
255
-
256
- dnsname = response.body['CreateLoadBalancerResult']['DNSName']
257
-
258
- # string it up to the id passed
259
- response = @elb.register_instances_with_load_balancer(id, elbname)
260
-
261
- # i hate this so much.
262
- @logger.debug(sprintf('sleeping[%s] to allow DNS propagation', self.passthrough[:dns_propagation_sleep]))
263
- sleep self.passthrough[:dns_propagation_sleep]
264
-
265
- self.passthrough[:created_elb] = elbname
266
-
267
- return dnsname
268
- end
269
-
270
- # TODO this will throw at the first error - should we catch?
271
- # run some commands, return an array of the output
272
- def aws_bootstrap (commands)
273
- self.aws_connect
274
- commands = (commands.is_a?(Array)) ? commands : [ commands ]
275
- output = Array.new
276
-
277
- commands.each do |command|
278
- output << self.run(command)
279
- end
280
-
281
- return output
282
- end
283
-
284
- def aws_connect
285
- return @ec2 unless @ec2.nil?
286
-
287
- config = {
288
- :provider => 'AWS',
289
- :region => self.passthrough[:region],
290
- :aws_access_key_id => self.passthrough[:key_id],
291
- :aws_secret_access_key => self.passthrough[:secret_key],
292
- }
293
-
294
- config[:endpoint] = self.passthrough[:ec2_endpoint] unless self.passthrough[:ec2_endpoint].nil?
295
- @ec2 = Fog::Compute.new(config)
296
- end
297
-
298
- def elb_connect
299
- return @elb unless @elb.nil?
300
-
301
- if self.passthrough[:elb_endpoint]
302
- endpoint = URI.parse(self.passthrough[:elb_endpoint])
303
- elsif self.passthrough[:ec2_endpoint]
304
- endpoint = URI.parse(self.passthrough[:ec2_endpoint])
305
- end
306
-
307
- config = {
308
- :region => self.passthrough[:region],
309
- :aws_access_key_id => self.passthrough[:key_id],
310
- :aws_secret_access_key => self.passthrough[:secret_key],
311
- }
312
-
313
- unless endpoint.nil?
314
- # if not specifying an endpoint, don't add to the config
315
- config[:host] = endpoint.host
316
- config[:path] = endpoint.path
317
- config[:port] = endpoint.port
318
- config[:scheme] = endpoint.scheme
319
- end
320
-
321
- @elb = Fog::AWS::ELB.new(config)
322
- end
323
-
324
- def find_ssh_elb (create_if_not_found = false, instance = aws_get_instance)
325
- # given an instance, see if there is already an ELB that it is connected to - and potentially create one
326
- self.elb_connect
327
- result = nil
328
-
329
- response = @elb.describe_load_balancers
330
- elbs = response.body['DescribeLoadBalancersResult']['LoadBalancerDescriptions']
331
-
332
- elbs.each do |elb|
333
- if elb['Instances'].member?(instance)
334
- result = elb['DNSName']
335
- break
336
- end
337
- end
338
-
339
- if create_if_not_found and result.nil?
340
- result = self.aws_connect_to_elb(instance, sprintf('%s-ssh', self.name))
341
- end
342
-
343
- result
344
-
345
- end
346
-
347
- end