rouster 0.7 → 0.41

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