automateit 0.71102 → 0.71103
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.
- data.tar.gz.sig +1 -2
- data/CHANGES.txt +14 -0
- data/Rakefile +27 -37
- data/bin/aissh +93 -0
- data/bin/aitag +7 -0
- data/lib/automateit.rb +2 -0
- data/lib/automateit/account_manager/etc.rb +3 -3
- data/lib/automateit/account_manager/nscd.rb +14 -11
- data/lib/automateit/address_manager/base.rb +2 -2
- data/lib/automateit/interpreter.rb +2 -19
- data/lib/automateit/package_manager.rb +2 -0
- data/lib/automateit/plugin/driver.rb +1 -1
- data/lib/automateit/plugin/manager.rb +1 -6
- data/lib/automateit/root.rb +1 -1
- data/lib/automateit/tag_manager/tag_parser.rb +14 -17
- data/lib/ext/shell_escape.rb +7 -0
- data/lib/nitpick.rb +29 -0
- data/spec/integration/account_manager_nscd_spec.rb +37 -0
- data/spec/integration/account_manager_spec.rb +39 -4
- data/spec/integration/shell_manager_spec.rb +135 -3
- data/spec/unit/address_manager_spec.rb +49 -0
- data/spec/unit/interpreter_spec.rb +32 -1
- data/spec/unit/nested_error_spec.rb +10 -0
- data/spec/unit/object_spec.rb +18 -0
- data/spec/unit/plugins_spec.rb +113 -0
- data/spec/unit/shell_escape_spec.rb +11 -0
- data/spec/unit/tag_manager_spec.rb +60 -15
- metadata +16 -3
- metadata.gz.sig +1 -2
- data/lib/automateit.rb.orig +0 -65
data/lib/nitpick.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Nitpick
|
2
|
+
module ClassMethods
|
3
|
+
# Use to manage nitpick message for debugging AutomateIt internals.
|
4
|
+
#
|
5
|
+
# Arguments:
|
6
|
+
# * nil -- Returns boolean of whether nitpick messages will be displayed.
|
7
|
+
# * Boolean -- Sets nitpick state.
|
8
|
+
# * String or Symbol -- Displays nitpick message if state is on.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
# nitpick true
|
12
|
+
# nitpick "I'm nitpicking"
|
13
|
+
def nitpick(value=nil)
|
14
|
+
case value
|
15
|
+
when NilClass: @nitpick
|
16
|
+
when TrueClass, FalseClass: @nitpick = value
|
17
|
+
when String, Symbol: puts "%% #{value}" if @nitpick
|
18
|
+
else raise TypeError.new("Unknown nitpick type: #{value.class}")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.included(receiver)
|
24
|
+
receiver.extend(ClassMethods)
|
25
|
+
end
|
26
|
+
|
27
|
+
include ClassMethods
|
28
|
+
extend ClassMethods
|
29
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "/../spec_helper.rb")
|
2
|
+
|
3
|
+
if not INTERPRETER.euid?
|
4
|
+
puts "NOTE: Can't check 'euid' on this platform, #{__FILE__}"
|
5
|
+
elsif not INTERPRETER.superuser?
|
6
|
+
puts "NOTE: Must be root to check #{__FILE__}"
|
7
|
+
elsif not INTERPRETER.account_manager.available?(:invalidate)
|
8
|
+
puts "NOTE: Can't check AccountManager::NSCD on this platform, #{__FILE__}"
|
9
|
+
else
|
10
|
+
describe AutomateIt::AccountManager::NSCD do
|
11
|
+
before(:all) do
|
12
|
+
@a = AutomateIt.new(:verbosity => Logger::WARN)
|
13
|
+
@m = @a.account_manager
|
14
|
+
@d = @m[:nscd]
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should know what NSCD databases are associated with passwd" do
|
18
|
+
for query in %w(user users passwd password)
|
19
|
+
@d.database_for(query).should == :passwd
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should know what NSCD databases are associated with group" do
|
24
|
+
for query in %w(group groups)
|
25
|
+
@d.database_for(query).should == :group
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should fail with invalid queries" do
|
30
|
+
lambda { @d.database_for(:stuff) }.should raise_error(ArgumentError)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should invalidate NSCD databases" do
|
34
|
+
@m.invalidate(:passwd).should be_true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -20,6 +20,7 @@ else
|
|
20
20
|
# Some OSes are limited to 8 character names :(
|
21
21
|
@username = "aitestus"
|
22
22
|
@groupname = "aitestgr"
|
23
|
+
@password = "automateit"
|
23
24
|
|
24
25
|
begin
|
25
26
|
raise "User named '#{@username}' found. If this isn't a real user, delete it so that the test can contineu. If this is a real user, change the spec to test with a user that shouldn't exist." if @m.users[@username]
|
@@ -68,6 +69,12 @@ else
|
|
68
69
|
return @m.add_group(@groupname, :members => @username)
|
69
70
|
end
|
70
71
|
|
72
|
+
unless INTERPRETER.tagged?("windows | darwin")
|
73
|
+
it "should have Etc support on Unix-like platforms" do
|
74
|
+
@m.driver_for(:users).should have_etc
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
71
78
|
it "should find root user" do
|
72
79
|
entry = @m.users["root"]
|
73
80
|
entry.should_not be_nil
|
@@ -78,6 +85,10 @@ else
|
|
78
85
|
@m.has_user?(@username).should be_false
|
79
86
|
end
|
80
87
|
|
88
|
+
it "should fail to change the password for a non-existent user" do
|
89
|
+
lambda{ @m.passwd(@username, "foo") }.should raise_error(ArgumentError)
|
90
|
+
end
|
91
|
+
|
81
92
|
it "should create a user" do
|
82
93
|
entry = add_user
|
83
94
|
|
@@ -253,9 +264,8 @@ else
|
|
253
264
|
end
|
254
265
|
end
|
255
266
|
|
256
|
-
|
267
|
+
def change_password_with(object)
|
257
268
|
add_user if @independent
|
258
|
-
pass = "automateit"
|
259
269
|
|
260
270
|
# TODO This isn't portable
|
261
271
|
def extract_pwent(username)
|
@@ -266,9 +276,34 @@ else
|
|
266
276
|
end
|
267
277
|
|
268
278
|
before = extract_pwent(@username)
|
269
|
-
|
279
|
+
object.passwd(@username, @password, :quiet => @quiet).should be_true
|
270
280
|
after = extract_pwent(@username)
|
271
|
-
|
281
|
+
|
282
|
+
before.should_not == after
|
283
|
+
end
|
284
|
+
|
285
|
+
it "should change password with default driver" do
|
286
|
+
change_password_with(@m)
|
287
|
+
end
|
288
|
+
|
289
|
+
if INTERPRETER.account_manager[:passwd_pty].available?
|
290
|
+
it "should change password with PTY driver" do
|
291
|
+
change_password_with(@m[:passwd_pty])
|
292
|
+
end
|
293
|
+
else
|
294
|
+
puts "NOTE: Can't check AccountManager::PasswdPTY on this platform, #{__FILE__}"
|
295
|
+
end
|
296
|
+
|
297
|
+
if INTERPRETER.account_manager[:passwd_expect].available?
|
298
|
+
it "should change password with Expect driver" do
|
299
|
+
change_password_with(@m[:passwd_expect])
|
300
|
+
end
|
301
|
+
else
|
302
|
+
puts "NOTE: Can't check AccountManager::PasswdExpect on this platform, #{__FILE__}"
|
303
|
+
end
|
304
|
+
|
305
|
+
it "should fail to change the password when given invalid arguents" do
|
306
|
+
lambda{ @m.passwd(Hash.new, "foo") }.should raise_error(TypeError)
|
272
307
|
end
|
273
308
|
|
274
309
|
it "should remove a user" do
|
@@ -304,7 +304,7 @@ describe AutomateIt::ShellManager, " in general" do
|
|
304
304
|
@a.render(:text => "Hello", :to => source_file2)
|
305
305
|
@a.mkdir(target_dir)
|
306
306
|
|
307
|
-
@a.
|
307
|
+
@a.cp_R(source_dir, target_dir).should == source_dir
|
308
308
|
File.exists?("meh/feh/file1").should be_true
|
309
309
|
File.exists?("meh/feh/file2").should be_true
|
310
310
|
|
@@ -312,6 +312,22 @@ describe AutomateIt::ShellManager, " in general" do
|
|
312
312
|
end
|
313
313
|
end
|
314
314
|
|
315
|
+
it "should copy files whose contents changed (cp)" do
|
316
|
+
@m.mktempdircd do
|
317
|
+
source = "file1"
|
318
|
+
target = "file2"
|
319
|
+
|
320
|
+
@a.render(:text => "Hello", :to => source)
|
321
|
+
@a.render(:text => "olleH", :to => target)
|
322
|
+
|
323
|
+
source_atime = File.atime(source)
|
324
|
+
source_mtime = File.mtime(source)
|
325
|
+
File.utime(source_atime, source_mtime, target)
|
326
|
+
|
327
|
+
@a.cp(source, target).should == source
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
315
331
|
it "should move files (mv)" do
|
316
332
|
@m.mktempdircd do
|
317
333
|
file1 = "foo"
|
@@ -357,6 +373,19 @@ describe AutomateIt::ShellManager, " in general" do
|
|
357
373
|
end
|
358
374
|
end
|
359
375
|
|
376
|
+
it "should delete forcefully (rm_f)" do
|
377
|
+
@m.mktempdircd do
|
378
|
+
target = "file1"
|
379
|
+
|
380
|
+
@m.touch(target)
|
381
|
+
@m.chmod(0000, target)
|
382
|
+
|
383
|
+
@m.rm(target, :force => true).should == [target]
|
384
|
+
|
385
|
+
File.exists?(target).should be_false
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
360
389
|
it "should delete recursively and forcefully (rm_rf)" do
|
361
390
|
@m.mktempdircd do
|
362
391
|
dir = "foo/bar"
|
@@ -505,9 +534,85 @@ describe AutomateIt::ShellManager, " when managing modes" do
|
|
505
534
|
end
|
506
535
|
end
|
507
536
|
|
508
|
-
|
509
|
-
|
537
|
+
it "should copy files whose mode changed (cp)" do
|
538
|
+
@m.mktempdircd do
|
539
|
+
source = "file1"
|
540
|
+
target = "file2"
|
541
|
+
|
542
|
+
@a.render(:text => "Hello", :to => source)
|
543
|
+
@a.cp(source, target)
|
544
|
+
|
545
|
+
File.chmod(0754, target)
|
546
|
+
source_atime = File.atime(source)
|
547
|
+
source_mtime = File.mtime(source)
|
548
|
+
File.utime(source_atime, source_mtime, target)
|
549
|
+
|
550
|
+
@a.cp(source, target, :preserve => true).should == source
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
it "should return umask value (umask)" do
|
555
|
+
@m.umask.should == File::umask
|
556
|
+
end
|
557
|
+
|
558
|
+
it "should set the default mask (umask)" do
|
559
|
+
@m.mktempdircd do
|
560
|
+
source = "file1"
|
561
|
+
previous_umask = File::umask
|
562
|
+
|
563
|
+
begin
|
564
|
+
@m.umask(0000)
|
565
|
+
File::umask.should_not == previous_umask
|
510
566
|
|
567
|
+
@a.touch(source)
|
568
|
+
File.stat(source).mode.should == 0100666
|
569
|
+
ensure
|
570
|
+
File::umask(previous_umask)
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
it "should set the default mask within block (umask)" do
|
576
|
+
@m.mktempdircd do
|
577
|
+
source = "file1"
|
578
|
+
previous_umask = File::umask
|
579
|
+
|
580
|
+
begin
|
581
|
+
@m.umask(0000) do
|
582
|
+
File::umask.should_not == previous_umask
|
583
|
+
|
584
|
+
@a.touch(source)
|
585
|
+
File.stat(source).mode.should == 0100666
|
586
|
+
end
|
587
|
+
|
588
|
+
File::umask.should == previous_umask
|
589
|
+
ensure
|
590
|
+
File::umask(previous_umask)
|
591
|
+
end
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
it "should reset umask correctly after an exception within a block (umask)" do
|
596
|
+
@m.mktempdircd do
|
597
|
+
previous_umask = File::umask
|
598
|
+
|
599
|
+
begin
|
600
|
+
begin
|
601
|
+
@m.umask(0000) do
|
602
|
+
File::umask.should_not == previous_umask
|
603
|
+
raise ArgumentError.new("OMFG")
|
604
|
+
end
|
605
|
+
rescue ArgumentError
|
606
|
+
# Error was expected
|
607
|
+
end
|
608
|
+
|
609
|
+
File::umask.should == previous_umask
|
610
|
+
ensure
|
611
|
+
File::umask(previous_umask)
|
612
|
+
end
|
613
|
+
end
|
614
|
+
end
|
615
|
+
end
|
511
616
|
end
|
512
617
|
|
513
618
|
describe AutomateIt::ShellManager, " when managing permissions" do
|
@@ -602,6 +707,25 @@ describe AutomateIt::ShellManager, " when managing permissions" do
|
|
602
707
|
(stat.gid == @gid).should be_true
|
603
708
|
end
|
604
709
|
end
|
710
|
+
|
711
|
+
it "should copy files whose owner changed (cp)" do
|
712
|
+
@m.mktempdircd do
|
713
|
+
source = "file1"
|
714
|
+
target = "file2"
|
715
|
+
|
716
|
+
@a.render(:text => "Hello", :to => source)
|
717
|
+
@a.cp(source, target)
|
718
|
+
|
719
|
+
# TODO not portable
|
720
|
+
File.chown(2, 2, target)
|
721
|
+
source_atime = File.atime(source)
|
722
|
+
source_mtime = File.mtime(source)
|
723
|
+
File.utime(source_atime, source_mtime, target)
|
724
|
+
|
725
|
+
@a.cp(source, target, :preserve => true).should == source
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
605
729
|
else
|
606
730
|
puts "NOTE: Must be root to check 'chown' in #{__FILE__}"
|
607
731
|
end
|
@@ -613,6 +737,10 @@ describe AutomateIt::ShellManager, " when managing hard links" do
|
|
613
737
|
else
|
614
738
|
it_should_behave_like "AutomateIt::ShellManager"
|
615
739
|
|
740
|
+
it "should provide links" do
|
741
|
+
@m.provides_link?.should be_true
|
742
|
+
end
|
743
|
+
|
616
744
|
it "should create hard links when needed (ln)" do
|
617
745
|
@m.mktempdircd do
|
618
746
|
source = "foo"
|
@@ -636,6 +764,10 @@ describe AutomateIt::ShellManager, " when managing symbolic links" do
|
|
636
764
|
else
|
637
765
|
it_should_behave_like "AutomateIt::ShellManager"
|
638
766
|
|
767
|
+
it "should provide symlinks" do
|
768
|
+
@m.provides_symlink?.should be_true
|
769
|
+
end
|
770
|
+
|
639
771
|
it "should create symlinks when needed (ln_s)" do
|
640
772
|
@m.mktempdircd do
|
641
773
|
source = "foo"
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "/../spec_helper.rb")
|
2
|
+
|
3
|
+
describe AutomateIt::AddressManager do
|
4
|
+
before(:all) do
|
5
|
+
@a = AutomateIt.new(:verbosity => Logger::WARN)
|
6
|
+
@m = @a.address_manager
|
7
|
+
@d = @a.address_manager.driver_for(:hostnames_for)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should be able to convert netmasks to CIDR addresses" do
|
11
|
+
@d.send(:mask_to_cidr, "255.255.255.0").should == 24
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be able to convert CIDR addresses to netmasks" do
|
15
|
+
@d.send(:cidr_to_mask, 24).should == "255.255.255.0"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should be able to convert decimals to binaries" do
|
19
|
+
@d.send(:dec2bin, 255).should == "11111111"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should be able to convert binaries to decimals" do
|
23
|
+
@d.send(:bin2dec, "11111111").should == 255
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should be able to derive interface and label from a device" do
|
27
|
+
@d.send(:_interface_and_label, :device => "eth0").should == "eth0"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should be able to derive interface and label from a device and alias" do
|
31
|
+
@d.send(:_interface_and_label, :device => "eth0", :label => "1").should == "eth0:1"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should fail to derive interface and lable from inadequate options" do
|
35
|
+
lambda { @d.send(:_interface_and_label, :label => "1") }.should raise_error(ArgumentError)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should be able to prepend arguments to ifconfig commands" do
|
39
|
+
@d.send(:_ifconfig_helper, :del,
|
40
|
+
{:address => "127.0.0.1", :device => "eth0", :label => "1"},
|
41
|
+
{:prepend => "inet"}).should == "ifconfig eth0:1 inet 127.0.0.1 down"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should be able to append arguments to ifconfig commands" do
|
45
|
+
@d.send(:_ifconfig_helper, :del,
|
46
|
+
{:address => "127.0.0.1", :device => "eth0", :label => "1"},
|
47
|
+
{:append => "unplumb"}).should == "ifconfig eth0:1 127.0.0.1 down unplumb"
|
48
|
+
end
|
49
|
+
end
|
@@ -2,13 +2,23 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "/../spec_helper.rb"
|
|
2
2
|
|
3
3
|
describe AutomateIt::Interpreter do
|
4
4
|
before(:all) do
|
5
|
-
@
|
5
|
+
@verbosity = Logger::WARN
|
6
|
+
@a = AutomateIt::Interpreter.new(:verbosity => @verbosity)
|
6
7
|
end
|
7
8
|
|
8
9
|
it "should have a logger" do
|
9
10
|
@a.log.should be_a_kind_of(Logger)
|
10
11
|
end
|
11
12
|
|
13
|
+
it "should be able to set a logger" do
|
14
|
+
@old_logger = @a.log
|
15
|
+
|
16
|
+
@a.log(QueuedLogger.new($stdout))
|
17
|
+
@a.log.level = @verbosity
|
18
|
+
|
19
|
+
@a.log.should_not == @old_logger
|
20
|
+
end
|
21
|
+
|
12
22
|
it "should provide a preview mode" do
|
13
23
|
@a.preview = true
|
14
24
|
@a.preview?.should be_true
|
@@ -21,6 +31,27 @@ describe AutomateIt::Interpreter do
|
|
21
31
|
@a.preview_for("answer"){42}.should == 42
|
22
32
|
@a.noop?.should be_false
|
23
33
|
@a.writing?.should be_true
|
34
|
+
|
35
|
+
@a.preview true
|
36
|
+
@a.preview?.should be_true
|
37
|
+
|
38
|
+
@a.noop false
|
39
|
+
@a.preview?.should be_false
|
40
|
+
|
41
|
+
@a.noop = true
|
42
|
+
@a.preview?.should be_true
|
43
|
+
|
44
|
+
@a.writing true
|
45
|
+
@a.preview?.should be_false
|
46
|
+
|
47
|
+
@a.writing = false
|
48
|
+
@a.preview?.should be_true
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should get and set values in the interpreter" do
|
52
|
+
@a.set("meow", :MEOW).should == :MEOW
|
53
|
+
@a.get("meow").should == :MEOW
|
54
|
+
@a.meow.should == :MEOW
|
24
55
|
end
|
25
56
|
|
26
57
|
it "should eval commands within Interpreter's context" do
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "/../spec_helper.rb")
|
2
|
+
|
3
|
+
describe NestedError do
|
4
|
+
it "should preserve the cause" do
|
5
|
+
e = NestedError.new("nested message", TypeError.new("cause message"))
|
6
|
+
e.message.should == "nested message"
|
7
|
+
e.cause.class.should == TypeError
|
8
|
+
e.cause.message.should == "cause message"
|
9
|
+
end
|
10
|
+
end
|