rouster 0.61 → 0.62
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +49 -2
- data/Rakefile +4 -0
- data/examples/openstack.rb +61 -0
- data/lib/rouster.rb +24 -2
- data/lib/rouster/deltas.rb +6 -0
- data/lib/rouster/testing.rb +4 -4
- data/lib/rouster/vagrant.rb +25 -8
- data/plugins/aws.rb +5 -5
- data/plugins/openstack.rb +129 -0
- data/test/functional/deltas/test_get_crontab.rb +19 -1
- data/test/functional/deltas/test_get_groups.rb +1 -1
- data/test/functional/test_new.rb +4 -4
- data/test/functional/test_passthroughs.rb +2 -2
- data/test/functional/test_validate_file.rb +51 -2
- data/test/unit/test_new.rb +1 -1
- data/test/unit/test_parse_ls_string.rb +1 -1
- data/test/unit/testing/resources/rhel-systemv +1 -0
- data/test/unit/testing/test_get_services.rb +6 -5
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8bce9804eb72b059c106b689793f06a26a55dcdb
|
4
|
+
data.tar.gz: 278dadb5f13e14a71ea84adecc751989f74ee810
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae9a583d9d11112ada1f3a17295f41f6d8d5563af4e389af2b1a090f9327a6b5441929199d205bcca341b770e68128cf0514ed7953b154f861651e5d0ab6067e
|
7
|
+
data.tar.gz: 2f012c9ca78fb82ff64c079ad5e262b77203021e74de3d0e20a1444a20d62633ba7119dbd962b57577fba4f77896930199cd61ba76e9555b85ec1787418fcc23
|
data/README.md
CHANGED
@@ -29,7 +29,7 @@ The first implementation of Rouster was in Perl, called [Salesforce::Vagrant](ht
|
|
29
29
|
* log4r
|
30
30
|
* net-scp
|
31
31
|
* net-ssh
|
32
|
-
* fog (only if using AWS)
|
32
|
+
* fog (only if using AWS or OpenStack)
|
33
33
|
|
34
34
|
Note: Rouster should work exactly the same on Windows as it does on \*nix and OSX (minus rouster/deltas.rb functionality, at least currently),
|
35
35
|
but no real testing has been done to confirm this. Please file issues as appropriate.
|
@@ -149,13 +149,14 @@ app.destroy()
|
|
149
149
|
|
150
150
|
### advanced instantiation (passthroughs!)
|
151
151
|
|
152
|
-
detailed options in ```examples/passthrough.rb``` and ```examples/
|
152
|
+
detailed options in ```examples/passthrough.rb```, ```examples/aws.rb``` and ```examples/openstack.rb```
|
153
153
|
|
154
154
|
since Rouster only requires an SSH connection to control a machine, why stop at Vagrant?
|
155
155
|
|
156
156
|
```rb
|
157
157
|
require 'rouster'
|
158
158
|
require 'rouster/plugins/aws'
|
159
|
+
require 'rouster/plugins/openstack'
|
159
160
|
|
160
161
|
# control the machine rouster itself is running on
|
161
162
|
local = Rouster.new(:name => 'local', :passthrough => { :type => :local } }
|
@@ -195,6 +196,38 @@ aws_start_me_up = Rouster.new(
|
|
195
196
|
}
|
196
197
|
)
|
197
198
|
|
199
|
+
# create a remote OpenStack instance
|
200
|
+
ostack = Rouster.new(
|
201
|
+
:name => 'ostack-testing',
|
202
|
+
:passthrough => {
|
203
|
+
:type => :openstack,
|
204
|
+
:openstack_auth_url => 'http://hostname.domain.com:5000/v2.0/tokens',
|
205
|
+
:openstack_username => 'some_console_user',
|
206
|
+
:openstack_tenant => 'tenant_id',
|
207
|
+
:user => 'some_ssh_userid',
|
208
|
+
:keypair => 'keypair_name',
|
209
|
+
:image_ref => 'c0340afb-577d-4db6-1234-aebdd6d1838f',
|
210
|
+
:flavor_ref => '547d9af5-096c-44a3-1234-7d23162556b8',
|
211
|
+
:openstack_api_key => 'some_api_key',
|
212
|
+
:key => '/path/to/private/key.pem',
|
213
|
+
},
|
214
|
+
:sudo => true, # false by default, enabling requires that sshd is not enforcing 'requiretty'
|
215
|
+
)
|
216
|
+
|
217
|
+
# control a running OpenStack instance
|
218
|
+
openstack_already_running = Rouster.new(
|
219
|
+
:name => 'ostack-copy',
|
220
|
+
:passthrough => {
|
221
|
+
:type => :openstack,
|
222
|
+
:openstack_auth_url => 'http://hostname.domain.com:5000/v2.0/tokens',
|
223
|
+
:openstack_username => 'some_console_user',
|
224
|
+
:openstack_tenant => 'tenant_id',
|
225
|
+
:user => 'ssh_user',
|
226
|
+
:keypair => 'keypair_name',
|
227
|
+
:instance => 'your-instance-id',
|
228
|
+
},
|
229
|
+
)
|
230
|
+
|
198
231
|
```
|
199
232
|
|
200
233
|
### functional puppet test
|
@@ -353,3 +386,17 @@ irb(main):004:0> pp (Rouster.new(:name => 'aws', :passthrough => { :type => :aws
|
|
353
386
|
...
|
354
387
|
]
|
355
388
|
```
|
389
|
+
|
390
|
+
## Openstack methods
|
391
|
+
|
392
|
+
```rb
|
393
|
+
[
|
394
|
+
:ostack_connect,
|
395
|
+
:ostack_describe_instance,
|
396
|
+
:ostack_destroy,
|
397
|
+
:ostack_get_instance_id,
|
398
|
+
:ostack_get_ip,
|
399
|
+
:ostack_status,
|
400
|
+
:ostack_up
|
401
|
+
]
|
402
|
+
```
|
data/Rakefile
CHANGED
@@ -0,0 +1,61 @@
|
|
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
|
data/lib/rouster.rb
CHANGED
@@ -12,7 +12,7 @@ require 'rouster/vagrant'
|
|
12
12
|
class Rouster
|
13
13
|
|
14
14
|
# sporadically updated version number
|
15
|
-
VERSION = 0.
|
15
|
+
VERSION = 0.62
|
16
16
|
|
17
17
|
# custom exceptions -- what else do we want them to include/do?
|
18
18
|
class ArgumentError < StandardError; end # thrown by methods that take parameters from users
|
@@ -145,6 +145,7 @@ class Rouster
|
|
145
145
|
aws_defaults = {
|
146
146
|
:ami => 'ami-7bdaa84b', # RHEL 6.5 x64 in us-west-2
|
147
147
|
:dns_propagation_sleep => 30, # how much time to wait after ELB creation before attempting to connect
|
148
|
+
:elb_cleanup => false,
|
148
149
|
:key_id => ENV['AWS_ACCESS_KEY_ID'],
|
149
150
|
:min_count => 1,
|
150
151
|
:max_count => 1,
|
@@ -187,9 +188,30 @@ class Rouster
|
|
187
188
|
|
188
189
|
raise ArgumentError.new('AWS passthrough requires valid :sshkey specification, should be path to private half') unless File.file?(@passthrough[:key])
|
189
190
|
@sshkey = @passthrough[:key]
|
191
|
+
elsif @passthrough[:type].eql?(:openstack)
|
192
|
+
@logger.debug(sprintf('instantiating an %s passthrough worker', @passthrough[:type]))
|
193
|
+
@sshkey = @passthrough[:key]
|
194
|
+
|
195
|
+
ostack_defaults = {
|
196
|
+
:ssh_port => 22,
|
197
|
+
}
|
198
|
+
@passthrough = ostack_defaults.merge(@passthrough)
|
190
199
|
|
200
|
+
[:openstack_auth_url, :openstack_username, :openstack_tenant, :openstack_api_key,
|
201
|
+
:key ].each do |r|
|
202
|
+
raise ArgumentError.new(sprintf('OpenStack passthrough requires %s specification', r)) if @passthrough[r].nil?
|
203
|
+
end
|
204
|
+
|
205
|
+
if @passthrough.has_key?(:image_ref)
|
206
|
+
@logger.debug(':image_ref specified, will start new Nova instance')
|
207
|
+
elsif @passthrough.has_key?(:instance)
|
208
|
+
@logger.debug(':instance specified, will connect to existing OpenStack instance')
|
209
|
+
inst_details = self.ostack_describe_instance(@passthrough[:instance])
|
210
|
+
raise ArgumentError.new(sprintf('No such instance found in OpenStack - %s', @passthrough[:instance])) if inst_details.nil?
|
211
|
+
@passthrough[:host] = inst_details.addresses["NextGen"][0]["addr"]
|
212
|
+
end
|
191
213
|
else
|
192
|
-
raise ArgumentError.new(sprintf('passthrough :type [%s] unknown, allowed: :aws, :local, :remote', @passthrough[:type]))
|
214
|
+
raise ArgumentError.new(sprintf('passthrough :type [%s] unknown, allowed: :aws, :openstack, :local, :remote', @passthrough[:type]))
|
193
215
|
end
|
194
216
|
|
195
217
|
else
|
data/lib/rouster/deltas.rb
CHANGED
@@ -70,6 +70,12 @@ class Rouster
|
|
70
70
|
next if line.match(/^#|^\s+$/)
|
71
71
|
elements = line.split("\s")
|
72
72
|
|
73
|
+
if elements.size < 5
|
74
|
+
# this is usually (only?) caused by ENV_VARIABLE=VALUE directives
|
75
|
+
@logger.debug(sprintf('line [%s] did not match format expectations for a crontab entry, skipping', line))
|
76
|
+
next
|
77
|
+
end
|
78
|
+
|
73
79
|
command = elements[5..elements.size].join(' ')
|
74
80
|
|
75
81
|
res[u] ||= Hash.new
|
data/lib/rouster/testing.rb
CHANGED
@@ -234,10 +234,10 @@ class Rouster
|
|
234
234
|
local = true
|
235
235
|
begin
|
236
236
|
self.run(sprintf("grep -c '%s' %s", regex, name))
|
237
|
-
rescue
|
237
|
+
rescue => e
|
238
238
|
local = false
|
239
239
|
end
|
240
|
-
|
240
|
+
break if local.false?
|
241
241
|
end
|
242
242
|
when :notcontains, :doesntcontain # TODO determine the appropriate attribute title here
|
243
243
|
v = v.class.eql?(Array) ? v : [v]
|
@@ -246,10 +246,10 @@ class Rouster
|
|
246
246
|
begin
|
247
247
|
self.run(sprintf("grep -c '%s' %s", regex, name))
|
248
248
|
local = false
|
249
|
-
rescue
|
249
|
+
rescue => e
|
250
250
|
local = true
|
251
251
|
end
|
252
|
-
|
252
|
+
break if local.false?
|
253
253
|
end
|
254
254
|
when :mode, :permissions
|
255
255
|
if properties.nil?
|
data/lib/rouster/vagrant.rb
CHANGED
@@ -48,8 +48,14 @@ class Rouster
|
|
48
48
|
@logger.info('up()')
|
49
49
|
|
50
50
|
# don't like putting this here, may be refactored
|
51
|
-
if self.is_passthrough?
|
52
|
-
self.
|
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
|
53
59
|
else
|
54
60
|
self.vagrant(sprintf('up %s', @name))
|
55
61
|
end
|
@@ -81,8 +87,14 @@ class Rouster
|
|
81
87
|
@logger.info('destroy()')
|
82
88
|
|
83
89
|
# don't like putting this here, may be refactored
|
84
|
-
if self.is_passthrough?
|
85
|
-
self.
|
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
|
86
98
|
else
|
87
99
|
self.vagrant(sprintf('destroy -f %s', @name))
|
88
100
|
end
|
@@ -109,8 +121,14 @@ class Rouster
|
|
109
121
|
|
110
122
|
# don't like putting this here, may be refactored
|
111
123
|
@logger.info('status()')
|
112
|
-
if self.is_passthrough?
|
113
|
-
|
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
|
114
132
|
else
|
115
133
|
self.vagrant(sprintf('status %s', @name))
|
116
134
|
|
@@ -274,5 +292,4 @@ class Rouster
|
|
274
292
|
end
|
275
293
|
end
|
276
294
|
|
277
|
-
|
278
|
-
end
|
295
|
+
end
|
data/plugins/aws.rb
CHANGED
@@ -176,11 +176,9 @@ class Rouster
|
|
176
176
|
|
177
177
|
server = @ec2.terminate_instances(self.aws_get_instance)
|
178
178
|
|
179
|
-
if
|
180
|
-
|
181
|
-
|
182
|
-
@logger.info(sprintf('deleting ELB[%s]', elb))
|
183
|
-
@elb.delete_load_balancer(elb)
|
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])
|
184
182
|
end
|
185
183
|
|
186
184
|
self.aws_status
|
@@ -264,6 +262,8 @@ class Rouster
|
|
264
262
|
@logger.debug(sprintf('sleeping[%s] to allow DNS propagation', self.passthrough[:dns_propagation_sleep]))
|
265
263
|
sleep self.passthrough[:dns_propagation_sleep]
|
266
264
|
|
265
|
+
self.passthrough[:created_elb] = elbname
|
266
|
+
|
267
267
|
return dnsname
|
268
268
|
end
|
269
269
|
|
@@ -0,0 +1,129 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
## plugins/openstack.rb - provide helper functions for Rouster objects running on OpenStack/Compute
|
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 :nova # expose OpenStack workers
|
12
|
+
attr_reader :instance_data # the result of the runInstances request
|
13
|
+
|
14
|
+
# return a hash containing meta-data items
|
15
|
+
def ostack_get_instance_id ()
|
16
|
+
# The instance id is kept in @passthrough[:instance] or
|
17
|
+
# can be obtained from @instance_data which has all instance
|
18
|
+
# details.
|
19
|
+
if ! @instance_data.nil? and ! @instance_data.id.nil?
|
20
|
+
return @instance_data.id # we already know the id
|
21
|
+
elsif @passthrough.has_key?(:instance)
|
22
|
+
return @passthrough[:instance] # we know the id we want
|
23
|
+
else
|
24
|
+
@logger.debug(sprintf('unable to determine id from instance_data[%s] or passthrough specification[%s]', @instance_data, @passthrough))
|
25
|
+
return nil # we don't have an id yet, likely a up() call
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def ostack_up
|
30
|
+
# wait for machine to transition to running state and become sshable (TODO maybe make the second half optional)
|
31
|
+
self.ostack_connect
|
32
|
+
# This will check if instance_id has been provided. If so, it will check on status of the instance.
|
33
|
+
status = self.status()
|
34
|
+
if status.eql?('running')
|
35
|
+
self.passthrough[:instance] = self.ostack_get_instance_id
|
36
|
+
@logger.debug(sprintf('Connecting to running instance [%s] while calling ostack_up()', self.passthrough[:instance]))
|
37
|
+
self.connect_ssh_tunnel
|
38
|
+
else
|
39
|
+
server = @nova.servers.create(:name => @name, :flavor_ref => @passthrough[:flavor_ref],
|
40
|
+
:image_ref => @passthrough[:image_ref], :key_name => @passthrough[:keypair])
|
41
|
+
server.wait_for { ready? }
|
42
|
+
@instance_data = server
|
43
|
+
self.passthrough[:host] = server.addresses["NextGen"][0]["addr"]
|
44
|
+
self.passthrough[:instance] = self.ostack_get_instance_id
|
45
|
+
end
|
46
|
+
self.passthrough[:instance]
|
47
|
+
end
|
48
|
+
|
49
|
+
def ostack_get_ip()
|
50
|
+
self.passthrough[:host]
|
51
|
+
end
|
52
|
+
|
53
|
+
def ostack_destroy
|
54
|
+
server = self.ostack_describe_instance
|
55
|
+
raise sprintf("instance[%s] not found by destroy()", self.ostack_get_instance_id) if server.nil?
|
56
|
+
server.destroy
|
57
|
+
@instance_data = nil
|
58
|
+
self.passthrough.delete(:instance)
|
59
|
+
end
|
60
|
+
|
61
|
+
def ostack_describe_instance(instance_id = ostack_get_instance_id)
|
62
|
+
|
63
|
+
if @cache_timeout
|
64
|
+
if @cache.has_key?(:ostack_describe_instance)
|
65
|
+
if (Time.now.to_i - @cache[:ostack_describe_instance][:time]) < @cache_timeout
|
66
|
+
@logger.debug(sprintf('using cached ostack_describe_instance?[%s] from [%s]', @cache[:ostack_describe_instance][:instance], @cache[:ostack_describe_instance][:time]))
|
67
|
+
return @cache[:ostack_describe_instance][:instance]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
# We don't have a instance.
|
72
|
+
return nil if instance_id.nil?
|
73
|
+
self.ostack_connect
|
74
|
+
response = @nova.servers.get(instance_id)
|
75
|
+
return nil if response.nil?
|
76
|
+
@instance_data = response
|
77
|
+
|
78
|
+
if @cache_timeout
|
79
|
+
@cache[:ostack_describe_instance] = Hash.new unless @cache[:ostack_describe_instance].class.eql?(Hash)
|
80
|
+
@cache[:ostack_describe_instance][:time] = Time.now.to_i
|
81
|
+
@cache[:ostack_describe_instance][:instance] = response
|
82
|
+
@logger.debug(sprintf('caching is_available_via_ssh?[%s] at [%s]', @cache[:ostack_describe_instance][:instance], @cache[:ostack_describe_instance][:time]))
|
83
|
+
end
|
84
|
+
|
85
|
+
@instance_data
|
86
|
+
end
|
87
|
+
|
88
|
+
def ostack_status
|
89
|
+
self.ostack_describe_instance
|
90
|
+
return 'not-created' if @instance_data.nil?
|
91
|
+
if @instance_data.state.eql?('ACTIVE')
|
92
|
+
# Make this consistent with AWS response.
|
93
|
+
return 'running'
|
94
|
+
else
|
95
|
+
return @instance_data.state
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
# TODO this will throw at the first error - should we catch?
|
101
|
+
# run some commands, return an array of the output
|
102
|
+
def ostack_bootstrap (commands)
|
103
|
+
self.ostack_connect
|
104
|
+
commands = (commands.is_a?(Array)) ? commands : [ commands ]
|
105
|
+
output = Array.new
|
106
|
+
|
107
|
+
commands.each do |command|
|
108
|
+
output << self.run(command)
|
109
|
+
end
|
110
|
+
|
111
|
+
return output
|
112
|
+
end
|
113
|
+
|
114
|
+
def ostack_connect
|
115
|
+
# Instantiates an Object which can communicate with OS Compute.
|
116
|
+
# No instance specific information is set at this time.
|
117
|
+
return @nova unless @nova.nil?
|
118
|
+
|
119
|
+
config = {
|
120
|
+
:provider => 'openstack', # OpenStack Fog provider
|
121
|
+
:openstack_auth_url => self.passthrough[:openstack_auth_url], # OpenStack Keystone endpoint
|
122
|
+
:openstack_username => self.passthrough[:openstack_username], # Your OpenStack Username
|
123
|
+
:openstack_tenant => self.passthrough[:openstack_tenant], # Your tenant id
|
124
|
+
:openstack_api_key => self.passthrough[:openstack_api_key], # Your OpenStack Password
|
125
|
+
:connection_options => self.passthrough[:connection_options] # Optional
|
126
|
+
}
|
127
|
+
@nova = Fog::Compute.new(config)
|
128
|
+
end
|
129
|
+
end
|
@@ -127,7 +127,7 @@ class TestDeltasGetCrontab < Test::Unit::TestCase
|
|
127
127
|
@app.run("crontab -u #{user} #{tmp}")
|
128
128
|
|
129
129
|
assert_nothing_raised do
|
130
|
-
res = @app.get_crontab(
|
130
|
+
res = @app.get_crontab(user)
|
131
131
|
end
|
132
132
|
|
133
133
|
assert_equal(Hash, res.class)
|
@@ -136,6 +136,24 @@ class TestDeltasGetCrontab < Test::Unit::TestCase
|
|
136
136
|
|
137
137
|
end
|
138
138
|
|
139
|
+
def test_non_matching_lines
|
140
|
+
res = nil
|
141
|
+
user = 'root'
|
142
|
+
tmp = sprintf('/tmp/rouster.tmp.crontab.%s.%s.%s', user, Time.now.to_i, $$)
|
143
|
+
|
144
|
+
@app.run("echo 'PATH=/sbin:/usr/bin:/usr/local/bin' > #{tmp}")
|
145
|
+
@app.run("echo '5 5 * * * echo #{user}' >> #{tmp}")
|
146
|
+
@app.run("crontab -u #{user} #{tmp}")
|
147
|
+
|
148
|
+
assert_nothing_raised do
|
149
|
+
res = @app.get_crontab(user, false)
|
150
|
+
end
|
151
|
+
|
152
|
+
assert_equal(Hash, res.class)
|
153
|
+
assert(res.has_key?('echo root'))
|
154
|
+
|
155
|
+
end
|
156
|
+
|
139
157
|
def teardown
|
140
158
|
@app = nil
|
141
159
|
end
|
@@ -54,7 +54,7 @@ class TestDeltasGetGroups < Test::Unit::TestCase
|
|
54
54
|
if @app.os_type.eql?(:redhat)
|
55
55
|
@app.run(sprintf('groupadd %s', new_group))
|
56
56
|
else
|
57
|
-
|
57
|
+
omit('only doing group creation on RHEL hosts')
|
58
58
|
end
|
59
59
|
|
60
60
|
assert_nothing_raised do
|
data/test/functional/test_new.rb
CHANGED
@@ -142,11 +142,11 @@ class TestNew < Test::Unit::TestCase
|
|
142
142
|
|
143
143
|
def test_good_remote_passthrough
|
144
144
|
|
145
|
-
|
145
|
+
omit('not running test_good_remote_passthrough, autogenerated a fake ssh key') if File.file?(@@user_sshkey) and File.read(@@user_sshkey).eql?("")
|
146
146
|
|
147
147
|
host = '127.0.0.1'
|
148
148
|
`ssh -i #{@@user_sshkey} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null #{host} exit` # if this succeeds, we want to exit immediately so we don't get hung
|
149
|
-
|
149
|
+
omit("found an ssh key, but it doesn't appear to be valid for this host") unless $?.success?
|
150
150
|
|
151
151
|
assert_nothing_raised do
|
152
152
|
@app = Rouster.new(
|
@@ -172,11 +172,11 @@ class TestNew < Test::Unit::TestCase
|
|
172
172
|
|
173
173
|
def test_paranoia_remote_passthrough
|
174
174
|
|
175
|
-
|
175
|
+
omit('not running test_good_remote_passthrough, autogenerated a fake ssh key') if File.file?(@@user_sshkey) and File.read(@@user_sshkey).eql?("")
|
176
176
|
|
177
177
|
host = '127.0.0.1'
|
178
178
|
`ssh -i #{@@user_sshkey} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null #{host} exit` # if this succeeds, we want to exit immediately so we don't get hung
|
179
|
-
|
179
|
+
omit("found an ssh key, but it doesn't appear to be valid for this host") unless $?.success?
|
180
180
|
|
181
181
|
assert_nothing_raised do
|
182
182
|
@app = Rouster.new(
|
@@ -54,11 +54,11 @@ class TestPassthroughs < Test::Unit::TestCase
|
|
54
54
|
|
55
55
|
def test_functional_remote_passthrough
|
56
56
|
|
57
|
-
|
57
|
+
omit('not running test_good_remote_passthrough, autogenerated a fake ssh key') if File.file?(@@user_sshkey) and File.read(@@user_sshkey).eql?("")
|
58
58
|
|
59
59
|
host = '127.0.0.1'
|
60
60
|
`ssh -i #{@@user_sshkey} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null #{host} exit`
|
61
|
-
|
61
|
+
omit("found an ssh key, but it doesn't appear to be valid for this host") unless $?.success?
|
62
62
|
|
63
63
|
assert_nothing_raised do
|
64
64
|
@remote = Rouster.new(
|
@@ -24,8 +24,6 @@ class TestValidateFileFunctional < Test::Unit::TestCase
|
|
24
24
|
end
|
25
25
|
|
26
26
|
# TODO tests
|
27
|
-
# :contains string
|
28
|
-
# :contains array
|
29
27
|
# :exists vs. :ensure -> what options are supported?
|
30
28
|
# :mode vs. :permissions
|
31
29
|
# :size
|
@@ -56,6 +54,57 @@ class TestValidateFileFunctional < Test::Unit::TestCase
|
|
56
54
|
|
57
55
|
end
|
58
56
|
|
57
|
+
def test_happy_contains_string
|
58
|
+
file = '/etc/hosts'
|
59
|
+
expectations = {
|
60
|
+
:ensure => 'file',
|
61
|
+
:contains => 'localhost'
|
62
|
+
}
|
63
|
+
|
64
|
+
assert_equal(true, @app.validate_file(file, expectations, false, true))
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_contains_array_ordering
|
69
|
+
|
70
|
+
file = '/etc/hosts'
|
71
|
+
|
72
|
+
expectation1 = {
|
73
|
+
:ensure => 'file',
|
74
|
+
:contains => [ 'localhost', 'foobar' ]
|
75
|
+
}
|
76
|
+
|
77
|
+
expectation2 = {
|
78
|
+
:ensure => 'file',
|
79
|
+
:contains => [ 'foobar', 'localhost' ]
|
80
|
+
}
|
81
|
+
|
82
|
+
[expectation1, expectation2].each do |expectation|
|
83
|
+
assert_equal(false, @app.validate_file(file, expectation, false, true))
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_not_contains_array_ordering
|
89
|
+
file = '/etc/hosts'
|
90
|
+
|
91
|
+
|
92
|
+
expectation1 = {
|
93
|
+
:ensure => 'file',
|
94
|
+
:notcontains => [ 'localhost', 'foobar']
|
95
|
+
}
|
96
|
+
|
97
|
+
expectation2 = {
|
98
|
+
:ensure => 'file',
|
99
|
+
:notcontains => [ 'foobar', 'localhost' ],
|
100
|
+
}
|
101
|
+
|
102
|
+
[expectation1, expectation2].each do |expectation|
|
103
|
+
assert_equal(false, @app.validate_file(file, expectation, false, true))
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
59
108
|
def test_happy_symlink
|
60
109
|
file = '/tmp/bang'
|
61
110
|
|
data/test/unit/test_new.rb
CHANGED
@@ -51,7 +51,7 @@ class TestNew < Test::Unit::TestCase
|
|
51
51
|
def test_default_overrides_aws_passthrough
|
52
52
|
|
53
53
|
key = sprintf('%s/.ssh/id_rsa', ENV['HOME'])
|
54
|
-
|
54
|
+
omit(sprintf('no suitable private key found at [%s]', key)) unless File.file?(key)
|
55
55
|
|
56
56
|
@app = Rouster.new(
|
57
57
|
:name => 'aws',
|
@@ -353,7 +353,7 @@ class TestParseLsString < Test::Unit::TestCase
|
|
353
353
|
def test_suid
|
354
354
|
str = "drwxr-sr-x 2 root root 4096 Oct 7 17:09 /etc/nagios/objects\n"
|
355
355
|
|
356
|
-
|
356
|
+
omit('need to improve (read: implement) actual suid support')
|
357
357
|
|
358
358
|
expectation = {
|
359
359
|
:directory? => true,
|
@@ -27,11 +27,12 @@ class TestUnitGetPackages < Test::Unit::TestCase
|
|
27
27
|
end
|
28
28
|
|
29
29
|
expected = {
|
30
|
-
'acpid'
|
31
|
-
'ip6tables'
|
32
|
-
'Kdump'
|
33
|
-
'mdmonitor'
|
34
|
-
'netconsole'
|
30
|
+
'acpid' => 'running', # acpid (pid 945) is running...
|
31
|
+
'ip6tables' => 'stopped', # ip6tables: Firewall is not running.
|
32
|
+
'Kdump' => 'stopped', # Kdump is not operational
|
33
|
+
'mdmonitor' => 'stopped', # mdmonitor is stopped
|
34
|
+
'netconsole' => 'stopped', # netconsole module not loaded
|
35
|
+
'redis-server' => 'running' # redis-server (pid 11285) is running...
|
35
36
|
}
|
36
37
|
|
37
38
|
expected.each_pair do |service,state|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rouster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.62'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Conor Horan-Kates
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -126,6 +126,7 @@ files:
|
|
126
126
|
- examples/bootstrap.rb
|
127
127
|
- examples/demo.rb
|
128
128
|
- examples/error.rb
|
129
|
+
- examples/openstack.rb
|
129
130
|
- examples/passthrough.rb
|
130
131
|
- lib/rouster.rb
|
131
132
|
- lib/rouster/deltas.rb
|
@@ -135,6 +136,7 @@ files:
|
|
135
136
|
- lib/rouster/vagrant.rb
|
136
137
|
- path_helper.rb
|
137
138
|
- plugins/aws.rb
|
139
|
+
- plugins/openstack.rb
|
138
140
|
- rouster.gemspec
|
139
141
|
- test/basic.rb
|
140
142
|
- test/functional/deltas/test_get_crontab.rb
|
@@ -207,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
207
209
|
version: 1.3.6
|
208
210
|
requirements: []
|
209
211
|
rubyforge_project: Rouster
|
210
|
-
rubygems_version: 2.4.
|
212
|
+
rubygems_version: 2.4.6
|
211
213
|
signing_key:
|
212
214
|
specification_version: 4
|
213
215
|
summary: Rouster is an abstraction layer for Vagrant
|