rouster 0.61 → 0.62
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.
- 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
|