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.
@@ -0,0 +1,7 @@
1
+ # Suraj Kurapati -- http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/261459
2
+ class String
3
+ # Transforms this string into an escaped POSIX shell argument.
4
+ def shell_escape
5
+ inspect.gsub(/\\(\d{3})/) {$1.to_i(8).chr}
6
+ end
7
+ end
@@ -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
- it "should change password" do
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
- @m.passwd(@username, pass, :quiet => @quiet).should be_true
279
+ object.passwd(@username, @password, :quiet => @quiet).should be_true
270
280
  after = extract_pwent(@username)
271
- before.should_not eql(after)
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.cp(source_dir, target_dir).should == source_dir
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
- #it "should set the default mask (umask)" # TODO
509
- end
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
- @a = AutomateIt::Interpreter.new(:verbosity => Logger::WARN)
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