rouster 0.5
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 +7 -0
- data/.gitignore +6 -0
- data/LICENSE +9 -0
- data/README.md +175 -0
- data/Rakefile +65 -0
- data/Vagrantfile +23 -0
- data/examples/bootstrap.rb +113 -0
- data/examples/demo.rb +71 -0
- data/examples/error.rb +30 -0
- data/lib/rouster.rb +737 -0
- data/lib/rouster/deltas.rb +481 -0
- data/lib/rouster/puppet.rb +398 -0
- data/lib/rouster/testing.rb +743 -0
- data/lib/rouster/tests.rb +596 -0
- data/path_helper.rb +21 -0
- data/rouster.gemspec +30 -0
- data/test/basic.rb +10 -0
- data/test/functional/deltas/test_get_crontab.rb +99 -0
- data/test/functional/deltas/test_get_groups.rb +48 -0
- data/test/functional/deltas/test_get_packages.rb +71 -0
- data/test/functional/deltas/test_get_ports.rb +119 -0
- data/test/functional/deltas/test_get_services.rb +43 -0
- data/test/functional/deltas/test_get_users.rb +45 -0
- data/test/functional/puppet/test_facter.rb +59 -0
- data/test/functional/test_caching.rb +124 -0
- data/test/functional/test_destroy.rb +51 -0
- data/test/functional/test_dirs.rb +88 -0
- data/test/functional/test_files.rb +64 -0
- data/test/functional/test_get.rb +76 -0
- data/test/functional/test_inspect.rb +31 -0
- data/test/functional/test_is_dir.rb +118 -0
- data/test/functional/test_is_file.rb +119 -0
- data/test/functional/test_new.rb +92 -0
- data/test/functional/test_put.rb +81 -0
- data/test/functional/test_rebuild.rb +49 -0
- data/test/functional/test_restart.rb +44 -0
- data/test/functional/test_run.rb +77 -0
- data/test/functional/test_status.rb +38 -0
- data/test/functional/test_suspend.rb +31 -0
- data/test/functional/test_up.rb +27 -0
- data/test/functional/test_validate_file.rb +30 -0
- data/test/puppet/manifests/default.pp +9 -0
- data/test/puppet/manifests/hiera.yaml +12 -0
- data/test/puppet/manifests/hieradata/common.json +3 -0
- data/test/puppet/manifests/hieradata/vagrant.json +3 -0
- data/test/puppet/manifests/manifest.pp +78 -0
- data/test/puppet/modules/role/manifests/ui.pp +5 -0
- data/test/puppet/test_apply.rb +149 -0
- data/test/puppet/test_roles.rb +186 -0
- data/test/tunnel_vs_scp.rb +41 -0
- data/test/unit/puppet/test_get_puppet_star.rb +68 -0
- data/test/unit/test_generate_unique_mac.rb +43 -0
- data/test/unit/test_new.rb +31 -0
- data/test/unit/test_parse_ls_string.rb +334 -0
- data/test/unit/test_traverse_up.rb +43 -0
- data/test/unit/testing/test_meets_constraint.rb +55 -0
- data/test/unit/testing/test_validate_file.rb +112 -0
- data/test/unit/testing/test_validate_group.rb +72 -0
- data/test/unit/testing/test_validate_package.rb +69 -0
- data/test/unit/testing/test_validate_port.rb +98 -0
- data/test/unit/testing/test_validate_service.rb +73 -0
- data/test/unit/testing/test_validate_user.rb +92 -0
- metadata +203 -0
@@ -0,0 +1,596 @@
|
|
1
|
+
require sprintf('%s/../../%s', File.dirname(File.expand_path(__FILE__)), 'path_helper')
|
2
|
+
|
3
|
+
require 'rouster/deltas'
|
4
|
+
|
5
|
+
class Rouster
|
6
|
+
|
7
|
+
##
|
8
|
+
# dir
|
9
|
+
#
|
10
|
+
# runs `ls -ld <dir>` and parses output, returns nil (if dir DNE or permission issue) or hash:
|
11
|
+
# {
|
12
|
+
# :directory? => boolean,
|
13
|
+
# :file? => boolean,
|
14
|
+
# :executable? => boolean, # based on user 'vagrant' context
|
15
|
+
# :writeable? => boolean, # based on user 'vagrant' context
|
16
|
+
# :readable? => boolean, # based on user 'vagrant' context
|
17
|
+
# :mode => mode, # 0-prefixed octal mode
|
18
|
+
# :name => name, # short name
|
19
|
+
# :owner => owner,
|
20
|
+
# :group => group,
|
21
|
+
# :size => size, # in bytes
|
22
|
+
# }
|
23
|
+
#
|
24
|
+
# parameters
|
25
|
+
# * <dir> - path of directory to act on, full path or relative to ~vagrant/
|
26
|
+
# * [cache] - boolean controlling whether to cache retrieved data, defaults to false
|
27
|
+
def dir(dir, cache=false)
|
28
|
+
|
29
|
+
if cache and self.deltas[:files].class.eql?(Hash) and ! self.deltas[:files][dir].nil?
|
30
|
+
return self.deltas[:files][dir]
|
31
|
+
end
|
32
|
+
|
33
|
+
if self.unittest and cache
|
34
|
+
# preventing a functional test fallthrough
|
35
|
+
return nil
|
36
|
+
end
|
37
|
+
|
38
|
+
begin
|
39
|
+
raw = self.run(sprintf('ls -ld %s', dir))
|
40
|
+
rescue Rouster::RemoteExecutionError
|
41
|
+
raw = self.get_output()
|
42
|
+
end
|
43
|
+
|
44
|
+
if raw.match(/No such file or directory/)
|
45
|
+
res = nil
|
46
|
+
elsif raw.match(/Permission denied/)
|
47
|
+
@log.info(sprintf('dir(%s) output[%s], try with sudo', dir, raw)) unless self.uses_sudo?
|
48
|
+
res = nil
|
49
|
+
else
|
50
|
+
res = parse_ls_string(raw)
|
51
|
+
end
|
52
|
+
|
53
|
+
if cache
|
54
|
+
self.deltas[:files] = Hash.new if self.deltas[:files].nil?
|
55
|
+
self.deltas[:files][dir] = res
|
56
|
+
end
|
57
|
+
|
58
|
+
res
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# dirs
|
63
|
+
#
|
64
|
+
# runs `find <dir> <recursive muckery> -type d -name '<wildcard>'`, and returns array of directories (fully qualified paths)
|
65
|
+
#
|
66
|
+
# parameters
|
67
|
+
# * <dir> - path to directory to act on, full path or relative to ~vagrant/
|
68
|
+
# * [wildcard] - glob of directories to match, defaults to '*'
|
69
|
+
# * [recursive] - boolean controlling whether or not to look in directories recursively, defaults to false
|
70
|
+
def dirs(dir, wildcard='*', insensitive=true, recursive=false)
|
71
|
+
# TODO use a numerical, not boolean value for 'recursive' -- and rename to 'depth' ?
|
72
|
+
raise InternalError.new(sprintf('invalid dir specified[%s]', dir)) unless self.is_dir?(dir)
|
73
|
+
|
74
|
+
raw = self.run(sprintf("find %s %s -type d %s '%s'", dir, recursive ? '' : '-maxdepth 1', insensitive ? '-iname' : '-name', wildcard))
|
75
|
+
res = Array.new
|
76
|
+
|
77
|
+
raw.split("\n").each do |line|
|
78
|
+
next if line.eql?(dir)
|
79
|
+
res.push(line)
|
80
|
+
end
|
81
|
+
|
82
|
+
res
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# file
|
87
|
+
#
|
88
|
+
# runs `ls -l <file>` and parses output, returns nil (if file DNE or permission issue) or hash:
|
89
|
+
# {
|
90
|
+
# :directory? => boolean,
|
91
|
+
# :file? => boolean,
|
92
|
+
# :executable? => boolean, # based on user 'vagrant' context
|
93
|
+
# :writeable? => boolean, # based on user 'vagrant' context
|
94
|
+
# :readable? => boolean, # based on user 'vagrant' context
|
95
|
+
# :mode => mode, # 0-prefixed octal mode
|
96
|
+
# :name => name, # short name
|
97
|
+
# :owner => owner,
|
98
|
+
# :group => group,
|
99
|
+
# :size => size, # in bytes
|
100
|
+
# }
|
101
|
+
#
|
102
|
+
# parameters
|
103
|
+
# * <file> - path of file to act on, full path or relative to ~vagrant/
|
104
|
+
# * [cache] - boolean controlling whether to cache retrieved data, defaults to false
|
105
|
+
def file(file, cache=false)
|
106
|
+
|
107
|
+
if cache and self.deltas[:files].class.eql?(Hash) and ! self.deltas[:files][file].nil?
|
108
|
+
return self.deltas[:files][file]
|
109
|
+
end
|
110
|
+
|
111
|
+
if self.unittest and cache
|
112
|
+
# preventing a functional test fallthrough
|
113
|
+
return nil
|
114
|
+
end
|
115
|
+
|
116
|
+
begin
|
117
|
+
raw = self.run(sprintf('ls -l %s', file))
|
118
|
+
rescue Rouster::RemoteExecutionError
|
119
|
+
raw = self.get_output()
|
120
|
+
end
|
121
|
+
|
122
|
+
if raw.match(/No such file or directory/)
|
123
|
+
@log.info(sprintf('is_file?(%s) output[%s], try with sudo', file, raw)) unless self.uses_sudo?
|
124
|
+
res = nil
|
125
|
+
elsif raw.match(/Permission denied/)
|
126
|
+
res = nil
|
127
|
+
else
|
128
|
+
res = parse_ls_string(raw)
|
129
|
+
end
|
130
|
+
|
131
|
+
if cache
|
132
|
+
self.deltas[:files] = Hash.new if self.deltas[:files].nil?
|
133
|
+
self.deltas[:files][file] = res
|
134
|
+
end
|
135
|
+
|
136
|
+
res
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# files
|
141
|
+
#
|
142
|
+
# runs `find <dir> <recursive muckery> -type f -name '<wildcard>'`, and reutns array of files (fullly qualified paths)
|
143
|
+
# parameters
|
144
|
+
# * <dir> - directory to look in, full path or relative to ~vagrant/
|
145
|
+
# * [wildcard] - glob of files to match, defaults to '*'
|
146
|
+
# * [recursive] - boolean controlling whether or not to look in directories recursively, defaults to false
|
147
|
+
def files(dir, wildcard='*', insensitive=true, recursive=false)
|
148
|
+
# TODO use a numerical, not boolean value for 'recursive'
|
149
|
+
raise InternalError.new(sprintf('invalid dir specified[%s]', dir)) unless self.is_dir?(dir)
|
150
|
+
|
151
|
+
raw = self.run(sprintf("find %s %s -type f %s '%s'", dir, recursive ? '' : '-maxdepth 1', insensitive ? '-iname' : '-name', wildcard))
|
152
|
+
res = Array.new
|
153
|
+
|
154
|
+
raw.split("\n").each do |line|
|
155
|
+
res.push(line)
|
156
|
+
end
|
157
|
+
|
158
|
+
res
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# is_dir?
|
163
|
+
#
|
164
|
+
# uses dir() to return boolean indicating whether parameter passed is a directory
|
165
|
+
#
|
166
|
+
# parameters
|
167
|
+
# * <dir> - path of directory to validate
|
168
|
+
def is_dir?(dir)
|
169
|
+
res = nil
|
170
|
+
begin
|
171
|
+
res = self.dir(dir)
|
172
|
+
rescue => e
|
173
|
+
return false
|
174
|
+
end
|
175
|
+
|
176
|
+
res.class.eql?(Hash) ? res[:directory?] : false
|
177
|
+
end
|
178
|
+
|
179
|
+
##
|
180
|
+
# is_executable?
|
181
|
+
#
|
182
|
+
# uses file() to return boolean indicating whether parameter passed is an executable file
|
183
|
+
#
|
184
|
+
# parameters
|
185
|
+
# * <filename> - path of filename to validate
|
186
|
+
# * [level] - string indicating 'u'ser, 'g'roup or 'o'ther context, defaults to 'u'
|
187
|
+
def is_executable?(filename, level='u')
|
188
|
+
res = nil
|
189
|
+
|
190
|
+
begin
|
191
|
+
res = file(filename)
|
192
|
+
rescue Rouster::InternalError
|
193
|
+
res = dir(filename)
|
194
|
+
end
|
195
|
+
|
196
|
+
# for cases that are directories, but don't throw exceptions
|
197
|
+
if res.nil? or res[:directory?]
|
198
|
+
res = dir(filename)
|
199
|
+
end
|
200
|
+
|
201
|
+
if res
|
202
|
+
array = res[:executable?]
|
203
|
+
|
204
|
+
case level
|
205
|
+
when 'u', 'U', 'user'
|
206
|
+
array[0]
|
207
|
+
when 'g', 'G', 'group'
|
208
|
+
array[1]
|
209
|
+
when 'o', 'O', 'other'
|
210
|
+
array[2]
|
211
|
+
else
|
212
|
+
raise InternalError.new(sprintf('unknown level[%s]'))
|
213
|
+
end
|
214
|
+
|
215
|
+
else
|
216
|
+
false
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
##
|
222
|
+
# is_file?
|
223
|
+
#
|
224
|
+
# uses file() to return boolean indicating whether parameter passed is a file
|
225
|
+
#
|
226
|
+
# parameters
|
227
|
+
# * <file> - path of filename to validate
|
228
|
+
def is_file?(file)
|
229
|
+
res = nil
|
230
|
+
|
231
|
+
begin
|
232
|
+
res = self.file(file)
|
233
|
+
rescue => e
|
234
|
+
return false
|
235
|
+
end
|
236
|
+
|
237
|
+
res.class.eql?(Hash) ? res[:file?] : false
|
238
|
+
end
|
239
|
+
|
240
|
+
##
|
241
|
+
# is_group?
|
242
|
+
#
|
243
|
+
# uses get_groups() to return boolean indicating whether parameter passed is a group
|
244
|
+
#
|
245
|
+
# parameters
|
246
|
+
# * <group> - name of group to validate
|
247
|
+
def is_group?(group)
|
248
|
+
groups = self.get_groups()
|
249
|
+
groups.has_key?(group)
|
250
|
+
end
|
251
|
+
|
252
|
+
##
|
253
|
+
# is_in_file?
|
254
|
+
#
|
255
|
+
# calls `grep -c '<regex>' <file>` and returns boolean for whether one or more matches are found in file
|
256
|
+
#
|
257
|
+
# parameters
|
258
|
+
# * <file> - path of filename to examine
|
259
|
+
# * <regex> - regular expression/string to be passed to grep
|
260
|
+
# * [scp] - downloads file to host machine before grepping (functionality not implemented, was planned when a new SSH connection was required for each run() command, not sure it is necessary any longer)
|
261
|
+
def is_in_file?(file, regex, scp=false)
|
262
|
+
|
263
|
+
res = nil
|
264
|
+
|
265
|
+
if scp
|
266
|
+
# download the file to a temporary directory
|
267
|
+
@log.warn('is_in_file? scp option not implemented yet')
|
268
|
+
end
|
269
|
+
|
270
|
+
begin
|
271
|
+
command = sprintf("grep -c '%s' %s", regex, file)
|
272
|
+
res = self.run(command)
|
273
|
+
rescue Rouster::RemoteExecutionError
|
274
|
+
return false
|
275
|
+
end
|
276
|
+
|
277
|
+
if res.nil?.false? and res.match(/^0/)
|
278
|
+
false
|
279
|
+
else
|
280
|
+
true
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
##
|
286
|
+
# is_in_path?
|
287
|
+
#
|
288
|
+
# runs `which <filename>`, returns boolean of whether the filename is exectuable and in $PATH
|
289
|
+
#
|
290
|
+
# parameters
|
291
|
+
# * <filename> - name of executable to validate
|
292
|
+
def is_in_path?(filename)
|
293
|
+
begin
|
294
|
+
self.run(sprintf('which %s', filename))
|
295
|
+
rescue Rouster::RemoteExecutionError
|
296
|
+
return false
|
297
|
+
end
|
298
|
+
|
299
|
+
true
|
300
|
+
end
|
301
|
+
|
302
|
+
##
|
303
|
+
# is_package?
|
304
|
+
#
|
305
|
+
# uses get_packages() to return boolean indicating whether passed parameter is an installed package
|
306
|
+
#
|
307
|
+
# parameters
|
308
|
+
# * <package> - name of package to validate
|
309
|
+
# * [cache] - boolean controlling whether to cache results from get_packages(), defaults to true (for performance)
|
310
|
+
def is_package?(package, cache=true)
|
311
|
+
# TODO should we implement something like is_package_version?()
|
312
|
+
packages = self.get_packages(cache)
|
313
|
+
packages.has_key?(package)
|
314
|
+
end
|
315
|
+
|
316
|
+
##
|
317
|
+
# is_port_active?
|
318
|
+
#
|
319
|
+
# uses get_ports() to return boolean indicating whether passed port is in use
|
320
|
+
#
|
321
|
+
# parameters
|
322
|
+
# * <port> - port number to validate
|
323
|
+
# * [proto] - specification of protocol to examine, defaults to tcp
|
324
|
+
# * [cache] - boolean controlling whether to cache get_ports() data, defaults to false
|
325
|
+
def is_port_active?(port, proto='tcp', cache=false)
|
326
|
+
# TODO is this the right name?
|
327
|
+
ports = self.get_ports(cache)
|
328
|
+
port = port.to_s
|
329
|
+
if ports[proto].class.eql?(Hash) and ports[proto].has_key?(port)
|
330
|
+
|
331
|
+
if proto.eql?('tcp')
|
332
|
+
['ACTIVE', 'ESTABLISHED', 'LISTEN']. each do |allowed|
|
333
|
+
return true if ports[proto][port][:address].values.member?(allowed)
|
334
|
+
end
|
335
|
+
else
|
336
|
+
return true
|
337
|
+
end
|
338
|
+
|
339
|
+
end
|
340
|
+
|
341
|
+
false
|
342
|
+
end
|
343
|
+
|
344
|
+
##
|
345
|
+
# is_port_open?
|
346
|
+
#
|
347
|
+
# uses get_ports() to return boolean indicating whether passed port is open
|
348
|
+
#
|
349
|
+
# parameters
|
350
|
+
# * <port> - port number to validate
|
351
|
+
# * [proto] - specification of protocol to examine, defaults to tcp
|
352
|
+
# * [cache] - boolean controlling whether to cache get_ports() data, defaults to false
|
353
|
+
def is_port_open?(port, proto='tcp', cache=false)
|
354
|
+
ports = self.get_ports(cache)
|
355
|
+
port = port.to_s
|
356
|
+
if ports[proto].class.eql?(Hash) and ports[proto].has_key?(port)
|
357
|
+
return false
|
358
|
+
end
|
359
|
+
|
360
|
+
true
|
361
|
+
end
|
362
|
+
|
363
|
+
##
|
364
|
+
# is_process_running?
|
365
|
+
#
|
366
|
+
# runs `ps ax | grep -c <process>` looking for more than 2 results
|
367
|
+
#
|
368
|
+
# parameters
|
369
|
+
# * <name> - name of process to look for
|
370
|
+
#
|
371
|
+
# supported OS
|
372
|
+
# * OSX
|
373
|
+
# * RedHat
|
374
|
+
# * Ubuntu
|
375
|
+
def is_process_running?(name)
|
376
|
+
# TODO support other flavors - this will work on RHEL and OSX
|
377
|
+
# TODO do better validation than just grepping for a matching filename
|
378
|
+
begin
|
379
|
+
|
380
|
+
os = self.os_type()
|
381
|
+
|
382
|
+
case os
|
383
|
+
when :redhat, :osx, :ubuntu, :debian
|
384
|
+
res = self.run(sprintf('ps ax | grep -c %s', name))
|
385
|
+
else
|
386
|
+
raise InternalError.new(sprintf('currently unable to determine running process list on OS[%s]', os))
|
387
|
+
end
|
388
|
+
|
389
|
+
rescue Rouster::RemoteExecutionError
|
390
|
+
return false
|
391
|
+
end
|
392
|
+
|
393
|
+
res.chomp.to_i > 2 # because of the weird way our process is run through the ssh tunnel
|
394
|
+
end
|
395
|
+
|
396
|
+
##
|
397
|
+
# is_readable?
|
398
|
+
#
|
399
|
+
# uses file() to return boolean indicating whether parameter passed is an readable file
|
400
|
+
#
|
401
|
+
# parameters
|
402
|
+
# * <filename> - path of filename to validate
|
403
|
+
# * [level] - string indicating 'u'ser, 'g'roup or 'o'ther context, defaults to 'u'
|
404
|
+
def is_readable?(filename, level='u')
|
405
|
+
res = nil
|
406
|
+
|
407
|
+
begin
|
408
|
+
res = file(filename)
|
409
|
+
rescue Rouster::InternalError
|
410
|
+
res = dir(filename)
|
411
|
+
end
|
412
|
+
|
413
|
+
# for cases that are directories, but don't throw exceptions
|
414
|
+
if res.nil? or res[:directory?]
|
415
|
+
res = dir(filename)
|
416
|
+
end
|
417
|
+
|
418
|
+
if res
|
419
|
+
array = res[:readable?]
|
420
|
+
|
421
|
+
case level
|
422
|
+
when 'u', 'U', 'user'
|
423
|
+
array[0]
|
424
|
+
when 'g', 'G', 'group'
|
425
|
+
array[1]
|
426
|
+
when 'o', 'O', 'other'
|
427
|
+
array[2]
|
428
|
+
else
|
429
|
+
raise InternalError.new(sprintf('unknown level[%s]'))
|
430
|
+
end
|
431
|
+
|
432
|
+
else
|
433
|
+
false
|
434
|
+
end
|
435
|
+
|
436
|
+
end
|
437
|
+
|
438
|
+
##
|
439
|
+
# is_service?
|
440
|
+
#
|
441
|
+
# uses get_services() to return boolean indicating whether passed parameter is an installed service
|
442
|
+
#
|
443
|
+
# parameters
|
444
|
+
# * <service> - name of service to validate
|
445
|
+
# * [cache] - boolean controlling whether to cache results from get_services(), defaults to true
|
446
|
+
def is_service?(service, cache=true)
|
447
|
+
services = self.get_services(cache)
|
448
|
+
services.has_key?(service)
|
449
|
+
end
|
450
|
+
|
451
|
+
|
452
|
+
##
|
453
|
+
# is_service_running?
|
454
|
+
#
|
455
|
+
# uses get_services() to return boolean indicating whether passed parameter is a running service
|
456
|
+
#
|
457
|
+
# parameters
|
458
|
+
# * <service> - name of service to validate
|
459
|
+
# * [cache] - boolean controlling whether to cache results from get_services(), defaults to false
|
460
|
+
def is_service_running?(service, cache=false)
|
461
|
+
services = self.get_services(cache)
|
462
|
+
|
463
|
+
if services.has_key?(service)
|
464
|
+
services[service].eql?('running').true?
|
465
|
+
else
|
466
|
+
false
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
##
|
471
|
+
# is_user?
|
472
|
+
#
|
473
|
+
# uses get_users() to return boolean indicating whether passed parameter is a user
|
474
|
+
#
|
475
|
+
# parameters
|
476
|
+
# * <user> - username to validate
|
477
|
+
# * [cache] - boolean controlling whether to cache results from get_users(), defaults to true
|
478
|
+
def is_user?(user, cache=true)
|
479
|
+
users = self.get_users(cache)
|
480
|
+
users.has_key?(user)
|
481
|
+
end
|
482
|
+
|
483
|
+
##
|
484
|
+
# is_user_in_group?
|
485
|
+
#
|
486
|
+
# uses get_users() and get_groups() to return boolean indicating whether passed user is in passed group
|
487
|
+
#
|
488
|
+
# parameters
|
489
|
+
# * <user> - username to validate
|
490
|
+
# * <group> - group expected to contain user
|
491
|
+
# * [cache] - boolean controlling whether to cache results from get_users() and get_groups(), defaults to true
|
492
|
+
def is_user_in_group?(user, group, cache=true)
|
493
|
+
# TODO can we scope this down to just use get_groups?
|
494
|
+
users = self.get_users(cache)
|
495
|
+
groups = self.get_groups(cache)
|
496
|
+
|
497
|
+
users.has_key?(user) and groups.has_key?(group) and groups[group][:users].member?(user)
|
498
|
+
end
|
499
|
+
|
500
|
+
##
|
501
|
+
# is_writeable?
|
502
|
+
#
|
503
|
+
# uses file() to return boolean indicating whether parameter passed is an executable file
|
504
|
+
#
|
505
|
+
# parameters
|
506
|
+
# * <filename> - path of filename to validate
|
507
|
+
# * [level] - string indicating 'u'ser, 'g'roup or 'o'ther context, defaults to 'u'
|
508
|
+
def is_writeable?(filename, level='u')
|
509
|
+
res = nil
|
510
|
+
|
511
|
+
begin
|
512
|
+
res = file(filename)
|
513
|
+
rescue Rouster::InternalError
|
514
|
+
res = dir(filename)
|
515
|
+
end
|
516
|
+
|
517
|
+
# for cases that are directories, but don't throw exceptions
|
518
|
+
if res.nil? or res[:directory?]
|
519
|
+
res = dir(filename)
|
520
|
+
end
|
521
|
+
|
522
|
+
if res
|
523
|
+
array = res[:writeable?]
|
524
|
+
|
525
|
+
case level
|
526
|
+
when 'u', 'U', 'user'
|
527
|
+
array[0]
|
528
|
+
when 'g', 'G', 'group'
|
529
|
+
array[1]
|
530
|
+
when 'o', 'O', 'other'
|
531
|
+
array[2]
|
532
|
+
else
|
533
|
+
raise InternalError.new(sprintf('unknown level[%s]'))
|
534
|
+
end
|
535
|
+
|
536
|
+
else
|
537
|
+
false
|
538
|
+
end
|
539
|
+
|
540
|
+
end
|
541
|
+
|
542
|
+
# non-test, helper methods
|
543
|
+
#private
|
544
|
+
def parse_ls_string(string)
|
545
|
+
# ht avaghti
|
546
|
+
|
547
|
+
res = Hash.new()
|
548
|
+
|
549
|
+
tokens = string.split(/\s+/)
|
550
|
+
|
551
|
+
# eww - do better here
|
552
|
+
modes = [ tokens[0][1..3], tokens[0][4..6], tokens[0][7..9] ]
|
553
|
+
mode = 0
|
554
|
+
|
555
|
+
# can't use modes.size here (or could, but would have to -1)
|
556
|
+
for i in 0..2 do
|
557
|
+
value = 0
|
558
|
+
element = modes[i]
|
559
|
+
|
560
|
+
for j in 0..2 do
|
561
|
+
chr = element[j].chr
|
562
|
+
case chr
|
563
|
+
when 'r'
|
564
|
+
value += 4
|
565
|
+
when 'w'
|
566
|
+
value += 2
|
567
|
+
when 'x', 't'
|
568
|
+
# is 't' really right here? copying Salesforce::Vagrant
|
569
|
+
value += 1
|
570
|
+
when '-'
|
571
|
+
# noop
|
572
|
+
else
|
573
|
+
raise InternalError.new(sprintf('unexpected character[%s] in string[%s]', chr, string))
|
574
|
+
end
|
575
|
+
|
576
|
+
end
|
577
|
+
|
578
|
+
mode = sprintf('%s%s', mode, value)
|
579
|
+
end
|
580
|
+
|
581
|
+
res[:mode] = mode
|
582
|
+
res[:name] = tokens[-1] # TODO better here: this does not support files/dirs with spaces
|
583
|
+
res[:owner] = tokens[2]
|
584
|
+
res[:group] = tokens[3]
|
585
|
+
res[:size] = tokens[4]
|
586
|
+
|
587
|
+
res[:directory?] = tokens[0][0].chr.eql?('d')
|
588
|
+
res[:file?] = ! res[:directory?]
|
589
|
+
res[:executable?] = [ tokens[0][3].chr.eql?('x'), tokens[0][6].chr.eql?('x'), tokens[0][9].chr.eql?('x') || tokens[0][9].chr.eql?('t') ]
|
590
|
+
res[:writeable?] = [ tokens[0][2].chr.eql?('w'), tokens[0][5].chr.eql?('w'), tokens[0][8].chr.eql?('w') ]
|
591
|
+
res[:readable?] = [ tokens[0][1].chr.eql?('r'), tokens[0][4].chr.eql?('r'), tokens[0][7].chr.eql?('r') ]
|
592
|
+
|
593
|
+
res
|
594
|
+
end
|
595
|
+
|
596
|
+
end
|